Ntrip szervíz refraktorálás, beállítások, jelszó secure store-ba
This commit is contained in:
@@ -17,7 +17,7 @@ class NtripSettingsController extends GetxController {
|
||||
final autoConnect = false.obs;
|
||||
final isBusy = false.obs;
|
||||
|
||||
final _secureStorage = const FlutterSecureStorage();
|
||||
NtripService get _service => NtripService.to;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@@ -26,79 +26,72 @@ class NtripSettingsController extends GetxController {
|
||||
}
|
||||
|
||||
Future<void> loadSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
hostController.text = _service.host.value;
|
||||
portController.text = _service.port.value.toString();
|
||||
mountPointController.text = _service.mountpoint.value;
|
||||
usernameController.text = _service.username.value;
|
||||
autoConnect.value = _service.autoConnect.value;
|
||||
passwordController.text = _service.password.value;
|
||||
}
|
||||
|
||||
hostController.text = prefs.getString('ntrip_host') ?? '';
|
||||
portController.text = prefs.getInt('ntrip_port')?.toString() ?? '2101';
|
||||
mountPointController.text = prefs.getString('ntrip_mountpoint') ?? '';
|
||||
usernameController.text = prefs.getString('ntrip_username') ?? '';
|
||||
autoConnect.value = prefs.getBool('ntrip_auto_connect') ?? false;
|
||||
Future<bool> _validateAndSave() async {
|
||||
if (!(formKey.currentState?.validate() ?? false)) return false;
|
||||
|
||||
passwordController.text =
|
||||
await _secureStorage.read(key: 'ntrip_password') ?? '';
|
||||
await _service.updateSettings(
|
||||
host: hostController.text,
|
||||
port: int.parse(portController.text.trim()),
|
||||
mountpoint: mountPointController.text,
|
||||
username: usernameController.text,
|
||||
password: passwordController.text,
|
||||
autoConnect: autoConnect.value);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> saveSettings() async {
|
||||
if (!(formKey.currentState?.validate() ?? false)) {
|
||||
return;
|
||||
}
|
||||
if (!await _validateAndSave()) return;
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
await prefs.setString('ntrip_host', hostController.text.trim());
|
||||
await prefs.setInt(
|
||||
'ntrip_port',
|
||||
int.parse(portController.text.trim()),
|
||||
);
|
||||
await prefs.setString(
|
||||
'ntrip_mountpoint',
|
||||
mountPointController.text.trim(),
|
||||
);
|
||||
await prefs.setString(
|
||||
'ntrip_username',
|
||||
usernameController.text.trim(),
|
||||
);
|
||||
await prefs.setBool(
|
||||
'ntrip_auto_connect',
|
||||
autoConnect.value,
|
||||
);
|
||||
|
||||
await _secureStorage.write(
|
||||
key: 'ntrip_password',
|
||||
value: passwordController.text,
|
||||
);
|
||||
Get.back();
|
||||
Get.snackbar('NTRIP', 'Beállítások elmentve.',
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
|
||||
Future<void> saveAndConnect() async {
|
||||
if (!(formKey.currentState?.validate() ?? false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isBusy.value = true;
|
||||
|
||||
await saveSettings();
|
||||
if (!await _validateAndSave()) return;
|
||||
|
||||
// await NtripService.to.connect(
|
||||
// host: hostController.text.trim(),
|
||||
// port: int.parse(portController.text.trim()),
|
||||
// mountPoint: mountPointController.text.trim(),
|
||||
// username: usernameController.text.trim(),
|
||||
// password: passwordController.text,
|
||||
// );
|
||||
// Ha már kapcsolódva vagyunk, előbb bontunk, hogy az új
|
||||
// beállításokkal épüljön újra a kapcsolat.
|
||||
if (_service.isConnected.value) {
|
||||
await _service.disconnect();
|
||||
}
|
||||
|
||||
await _service.connect();
|
||||
|
||||
Get.back();
|
||||
|
||||
Get.snackbar(
|
||||
'NTRIP',
|
||||
'Kapcsolódás elindítva.',
|
||||
'Kapcsolódva: ${_service.mountpoint.value}',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: const Color(0xFF2E7D32),
|
||||
colorText: const Color(0xFFFFFFFF),
|
||||
);
|
||||
} on NtripException catch (e) {
|
||||
Get.snackbar(
|
||||
'NTRIP hiba',
|
||||
e.message,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: const Color(0xFFB71C1C),
|
||||
colorText: const Color(0xFFFFFFFF),
|
||||
);
|
||||
} catch (e) {
|
||||
Get.snackbar(
|
||||
'NTRIP hiba',
|
||||
e.toString(),
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: const Color(0xFFB71C1C),
|
||||
colorText: const Color(0xFFFFFFFF),
|
||||
);
|
||||
} finally {
|
||||
isBusy.value = false;
|
||||
@@ -108,7 +101,7 @@ class NtripSettingsController extends GetxController {
|
||||
Future<void> disconnect() async {
|
||||
try {
|
||||
isBusy.value = true;
|
||||
await NtripService.to.disconnect();
|
||||
await _service.disconnect();
|
||||
} finally {
|
||||
isBusy.value = false;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,18 @@ import '../controllers/ntrip_settings_controller.dart';
|
||||
class NtripSettingsSheet extends GetView<NtripSettingsController> {
|
||||
const NtripSettingsSheet({super.key});
|
||||
|
||||
static Future<void> show() async {
|
||||
Get.put(NtripSettingsController());
|
||||
try {
|
||||
await Get.bottomSheet(
|
||||
const NtripSettingsSheet(),
|
||||
isScrollControlled: true,
|
||||
);
|
||||
} finally {
|
||||
Get.delete<NtripSettingsController>();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
@@ -97,7 +109,7 @@ class NtripSettingsSheet extends GetView<NtripSettingsController> {
|
||||
controller: controller.mountPointController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Mountpoint',
|
||||
hintText: 'pl. BUDAPEST_RTCM32',
|
||||
hintText: 'pl. SGO_RTK3.2',
|
||||
prefixIcon: Icon(Icons.place),
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:terepi_seged/pages/ntrip_settings/presentation/views/ntrip_settings_sheet.dart';
|
||||
import 'package:terepi_seged/services/gnss/gnss_connection.dart';
|
||||
import 'package:terepi_seged/services/gnss/gnss_device_service.dart';
|
||||
import 'package:terepi_seged/services/gnss/gnss_service.dart';
|
||||
import 'package:terepi_seged/services/ntrip_service.dart';
|
||||
import 'package:terepi_seged/widgets/gnss_device_picker_dialog.dart';
|
||||
|
||||
/// Központi beállítások oldal.
|
||||
///
|
||||
/// Material 3 konvenciók:
|
||||
/// - egyetlen görgethető lista, szekciókra bontva
|
||||
/// - az aktuális érték a subtitle-ben látszik
|
||||
/// - kapcsolók azonnal érvényesülnek (nincs "Mentés" gomb ezen a szinten)
|
||||
/// - űrlap-jellegű beállítás (NTRIP) al-sheetre megy, explicit mentéssel
|
||||
///
|
||||
/// Bővítés: új ListTile / SwitchListTile a megfelelő szekcióba,
|
||||
/// vagy új _SettingsSection, ha új témakör jön.
|
||||
class SettingsView extends StatelessWidget {
|
||||
const SettingsView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Beállítások'),
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
// ── Kapcsolat ─────────────────────────────────────────────
|
||||
const _SettingsSection('Kapcsolat'),
|
||||
|
||||
// NTRIP — subtitle mutatja az aktuális konfigurációt,
|
||||
// a trailing pötty a kapcsolat állapotát.
|
||||
Obx(() {
|
||||
final ntrip = NtripService.to;
|
||||
final configured = ntrip.hasCompleteSettings;
|
||||
final subtitle = configured
|
||||
? '${ntrip.host.value} · ${ntrip.mountpoint.value}'
|
||||
: 'Nincs beállítva';
|
||||
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.cell_tower),
|
||||
title: const Text('NTRIP korrekció'),
|
||||
subtitle: Text(subtitle, style: const TextStyle(fontSize: 12)),
|
||||
trailing: Icon(
|
||||
Icons.circle,
|
||||
size: 12,
|
||||
color: ntrip.isConnected.value
|
||||
? Colors.green
|
||||
: (configured ? Colors.grey : Colors.orange),
|
||||
),
|
||||
onTap: NtripSettingsSheet.show,
|
||||
);
|
||||
}),
|
||||
|
||||
// GNSS eszköz — ugyanaz a választó, mint a drawer-ben.
|
||||
Obx(() {
|
||||
final device = GnssDeviceService.to.selectedDevice.value;
|
||||
final connected = GnssService.to.connectionState.value ==
|
||||
GnssConnectionState.connected;
|
||||
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
device?.type == GnssConnectionType.ble
|
||||
? Icons.bluetooth_searching
|
||||
: device?.type == GnssConnectionType.phoneGps
|
||||
? Icons.phone_android
|
||||
: Icons.bluetooth,
|
||||
),
|
||||
title: const Text('GNSS eszköz'),
|
||||
subtitle: Text(
|
||||
device?.name ?? 'Nincs kiválasztva',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
trailing: Icon(
|
||||
Icons.circle,
|
||||
size: 12,
|
||||
color: connected ? Colors.green : Colors.grey,
|
||||
),
|
||||
onTap: GnssDevicePickerDialog.show,
|
||||
);
|
||||
}),
|
||||
|
||||
const Divider(height: 24),
|
||||
|
||||
// ── Térkép ────────────────────────────────────────────────
|
||||
const _SettingsSection('Térkép'),
|
||||
|
||||
// Helykitöltők — a onTap kitöltésével élesíthetők,
|
||||
// az enabled: false vizuálisan is jelzi, hogy még nem aktívak.
|
||||
const ListTile(
|
||||
leading: Icon(Icons.map_outlined),
|
||||
title: Text('Alaptérkép'),
|
||||
subtitle: Text('OpenStreetMap', style: TextStyle(fontSize: 12)),
|
||||
enabled: false,
|
||||
),
|
||||
const ListTile(
|
||||
leading: Icon(Icons.straighten),
|
||||
title: Text('Koordináta-megjelenítés'),
|
||||
subtitle: Text('EOV', style: TextStyle(fontSize: 12)),
|
||||
enabled: false,
|
||||
),
|
||||
|
||||
const Divider(height: 24),
|
||||
|
||||
// ── Névjegy ───────────────────────────────────────────────
|
||||
const _SettingsSection('Névjegy'),
|
||||
|
||||
FutureBuilder<PackageInfo>(
|
||||
future: PackageInfo.fromPlatform(),
|
||||
builder: (_, snap) => ListTile(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: const Text('Verzió'),
|
||||
subtitle: Text(
|
||||
snap.hasData
|
||||
? '${snap.data!.version} (build ${snap.data!.buildNumber})'
|
||||
: '…',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.description_outlined),
|
||||
title: const Text('Nyílt forráskódú licencek'),
|
||||
onTap: () => showLicensePage(
|
||||
context: context,
|
||||
applicationName: 'Terepi Segéd',
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Szekció felirat — ugyanaz a stílus, mint a drawer-ben ───────────
|
||||
|
||||
class _SettingsSection extends StatelessWidget {
|
||||
final String text;
|
||||
const _SettingsSection(this.text);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 4),
|
||||
child: Text(
|
||||
text.toUpperCase(),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey.shade500,
|
||||
letterSpacing: 0.8,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user