import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:winui_n2n/edge_state.dart'; import 'package:winui_n2n/saved_connection.dart'; import 'package:winui_n2n/shared_pref_singleton.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; TextEditingController _supernodeController = TextEditingController(); TextEditingController _communityController = TextEditingController(); TextEditingController _keyController = TextEditingController(); TextEditingController _selfAddressController = TextEditingController(); TextEditingController _configNameController = TextEditingController(); class ControlPage extends StatefulWidget { const ControlPage({super.key}); @override State createState() => _ControlPageState(); } class _ControlPageState extends State { bool _edgeConnecting = false; @override void initState() { super.initState(); } @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.all(8.0), child: SizedBox( width: 300, child: TextField( controller: _supernodeController, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: AppLocalizations.of(context)!.n2nSuperNode, ), ), ), ), Padding( padding: const EdgeInsets.all(8.0), child: SizedBox( width: 300, child: TextField( controller: _communityController, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: AppLocalizations.of(context)!.n2nCommunity, ), ), ), ), Padding( padding: const EdgeInsets.all(8.0), child: SizedBox( width: 300, child: TextField( controller: _keyController, obscureText: true, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: AppLocalizations.of(context)!.n2nCommunityKey, ), ), ), ), Padding( padding: const EdgeInsets.all(8.0), child: SizedBox( width: 300, child: TextField( controller: _selfAddressController, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: AppLocalizations.of(context)!.n2nSelfAddress, ), ), ), ), Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () => showDialog( context: context, builder: (BuildContext context) => Dialog( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 350, child: TextField( controller: _configNameController, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: AppLocalizations.of(context)! .configNameComment, ), ), ), const SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ TextButton( onPressed: () { List nodeList = jsonDecode( SharedPrefSingleton().savedConnection); List nodes = nodeList .cast>() .map((nodeData) => SavedConnection.fromJson(nodeData)) .toList(); nodes.add(SavedConnection( _configNameController.text, _supernodeController.text, _communityController.text, _keyController.text, _selfAddressController.text, )); List> nodeMaps = nodes .map((node) => node.toJson()) .toList(); String jsonString = jsonEncode(nodeMaps); SharedPrefSingleton() .setSavedConnection(jsonString); Navigator.pop(context); return; }, child: Text(AppLocalizations.of(context)!.save)), TextButton( onPressed: () { Navigator.pop(context); return; }, child: Text(AppLocalizations.of(context)!.cancel), ), ], ), ], ), ), ), ), child: Text(AppLocalizations.of(context)!.saveConfig), ), ElevatedButton( onPressed: _edgeConnecting ? null : () async { if (EdgeState.instance.isRunning) { // Turn on firewall if (SharedPrefSingleton().autoFirewall) { await Process.run( 'netsh.exe', [ 'advfirewall', 'set', 'allprofiles', 'state', 'on', ], ); } // Kill the edge process if (EdgeState.instance.process != null) { if (EdgeState.instance.process!.kill()) { EdgeState.instance.isRunning = false; EdgeState.instance.process = null; setState(() {}); } } } else if (!EdgeState.instance.isRunning) { // Block user click setState(() { _edgeConnecting = true; }); // Check for tap device final findTapResult = await Process.run( './tools/driver/devcon.exe', ['hwids', 'tap0901'], ); if (findTapResult.stdout .toString() .contains('No matching devices found.')) { // Install tap device EdgeState.instance.logger .addLog('Tap device not found, installing tap'); debugPrint('Tap device not found, installing tap'); final installTapResult = await Process.run( './tools/driver/devcon.exe', [ 'install', './tools/driver/OemVista.inf', 'tap0901', ], ); if (installTapResult.stdout.toString().contains( 'Tap driver installed successfully.') || installTapResult.stdout.toString().contains( 'Drivers installed successfully.')) { EdgeState.instance.logger .addLog('Tap driver installed successfully.'); debugPrint('Tap driver installed successfully.'); } else { EdgeState.instance.logger .addLog('Tap driver install failed.'); debugPrint('Tap driver install failed.'); } } else { // Ignore. EdgeState.instance.logger .addLog('Tap device already installed'); debugPrint('Tap device already installed'); } // Close firewall if (SharedPrefSingleton().autoFirewall) { await Process.run( 'netsh.exe', [ 'advfirewall', 'set', 'allprofiles', 'state', 'off', ], ); } // Start the edge // edge -c -k -a -r -l Process.start( './tools/edge/edge.exe', [ '-l', _supernodeController.text, '-c', _communityController.text, '-k', _keyController.text, '-a', _selfAddressController.text, '-r', ], ).then((process) async { EdgeState.instance.isRunning = true; EdgeState.instance.process = process; EdgeState.instance.logger .addLog('edge.exe starting'); debugPrint('edge.exe starting'); setState(() { _edgeConnecting = false; }); process.stdout .transform(const SystemEncoding().decoder) .listen((data) { debugPrint('stdout: $data'); EdgeState.instance.logger.addLog(data); }); await process.exitCode; EdgeState.instance.isRunning = false; EdgeState.instance.process = null; EdgeState.instance.logger .addLog('edge.exe closing'); debugPrint('edge.exe closing'); if (!mounted) return; setState(() {}); }); } }, child: EdgeState.instance.isRunning ? Text(AppLocalizations.of(context)!.disconnect) : Text(AppLocalizations.of(context)!.connect), ), TextButton( onPressed: () { SavedConnection? selectedSavedConnection; showDialog( context: context, builder: (BuildContext context) => Dialog( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ Builder(builder: (context) { String jsonString = SharedPrefSingleton().savedConnection; List nodeList = jsonDecode(jsonString); List nodes = nodeList .cast>() .map((nodeData) => SavedConnection.fromJson(nodeData)) .toList(); return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { return DropdownButton( value: selectedSavedConnection, items: nodes.map< DropdownMenuItem< SavedConnection>>( (SavedConnection value) { return DropdownMenuItem< SavedConnection>( value: value, child: Text(value.name), ); }).toList(), onChanged: (value) { setState( () { selectedSavedConnection = value!; }, ); }); }); }), const SizedBox(height: 15), Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () { if (selectedSavedConnection != null) { _supernodeController.text = selectedSavedConnection!.supernode; _communityController.text = selectedSavedConnection!.community; _keyController.text = selectedSavedConnection! .communityKey; _selfAddressController.text = selectedSavedConnection! .selfAddress; } Navigator.pop(context); return; }, child: Text(AppLocalizations.of(context)!.use), ), TextButton( onPressed: () { if (selectedSavedConnection == null) { Navigator.pop(context); return; } String jsonString = SharedPrefSingleton().savedConnection; List nodeList = jsonDecode(jsonString); List nodes = nodeList .cast>() .map((nodeData) => SavedConnection.fromJson( nodeData)) .toList(); nodes.removeWhere( (element) { return element.name == selectedSavedConnection!.name; }, ); List> nodeMaps = nodes .map((node) => node.toJson()) .toList(); SharedPrefSingleton() .setSavedConnection( jsonEncode(nodeMaps)) .then((onValue) { Navigator.pop(context); return; }); }, child: Text( AppLocalizations.of(context)!.delete), ), TextButton( onPressed: () { Navigator.pop(context); return; }, child: Text( AppLocalizations.of(context)!.cancel), ), ], ), ], ), ), ), ); }, child: Text(AppLocalizations.of(context)!.useConfig)), ], ), ), ], ); } }