| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | import 'dart:convert'; | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | import 'dart:io'; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-15 17:31:28 +08:00
										 |  |  | import 'package:file_picker/file_picker.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | import 'package:flutter/services.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | import 'package:flutter_hbb/common.dart'; | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  | import 'package:flutter_hbb/consts.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-23 18:28:40 +08:00
										 |  |  | import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | import 'package:flutter_hbb/models/platform_model.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/models/server_model.dart'; | 
					
						
							|  |  |  | import 'package:get/get.dart'; | 
					
						
							|  |  |  | import 'package:provider/provider.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-15 17:31:28 +08:00
										 |  |  | import 'package:url_launcher/url_launcher.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | import 'package:url_launcher/url_launcher_string.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  | import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 21:52:08 +08:00
										 |  |  | import '../../common/widgets/dialog.dart'; | 
					
						
							| 
									
										
										
										
											2023-01-08 23:30:34 +09:00
										 |  |  | import '../../common/widgets/login.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-16 21:52:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | const double _kTabWidth = 235; | 
					
						
							|  |  |  | const double _kTabHeight = 42; | 
					
						
							| 
									
										
										
										
											2022-09-24 09:58:57 +08:00
										 |  |  | const double _kCardFixedWidth = 540; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | const double _kCardLeftMargin = 15; | 
					
						
							|  |  |  | const double _kContentHMargin = 15; | 
					
						
							|  |  |  | const double _kContentHSubMargin = _kContentHMargin + 33; | 
					
						
							|  |  |  | const double _kCheckBoxLeftMargin = 10; | 
					
						
							|  |  |  | const double _kRadioLeftMargin = 10; | 
					
						
							|  |  |  | const double _kListViewBottomMargin = 15; | 
					
						
							|  |  |  | const double _kTitleFontSize = 20; | 
					
						
							|  |  |  | const double _kContentFontSize = 15; | 
					
						
							|  |  |  | const Color _accentColor = MyTheme.accent; | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  | const String _kSettingPageControllerTag = 'settingPageController'; | 
					
						
							|  |  |  | const String _kSettingPageIndexTag = 'settingPageIndex'; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class _TabInfo { | 
					
						
							|  |  |  |   late final String label; | 
					
						
							|  |  |  |   late final IconData unselected; | 
					
						
							|  |  |  |   late final IconData selected; | 
					
						
							| 
									
										
										
										
											2022-08-23 19:49:11 +08:00
										 |  |  |   _TabInfo(this.label, this.unselected, this.selected); | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-08-11 16:03:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class DesktopSettingPage extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-09-23 18:28:40 +08:00
										 |  |  |   final int initialPage; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const DesktopSettingPage({Key? key, required this.initialPage}) | 
					
						
							|  |  |  |       : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-08-11 16:03:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<DesktopSettingPage> createState() => _DesktopSettingPageState(); | 
					
						
							| 
									
										
										
										
											2022-09-23 18:28:40 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   static void switch2page(int page) { | 
					
						
							|  |  |  |     if (page >= 5) return; | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       if (Get.isRegistered<PageController>(tag: _kSettingPageControllerTag)) { | 
					
						
							|  |  |  |         DesktopTabPage.onAddSetting(initialPage: page); | 
					
						
							|  |  |  |         PageController controller = Get.find(tag: _kSettingPageControllerTag); | 
					
						
							|  |  |  |         RxInt selectedIndex = Get.find(tag: _kSettingPageIndexTag); | 
					
						
							|  |  |  |         selectedIndex.value = page; | 
					
						
							|  |  |  |         controller.jumpToPage(page); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         DesktopTabPage.onAddSetting(initialPage: page); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							| 
									
										
										
										
											2022-12-09 10:49:47 +08:00
										 |  |  |       debugPrintStack(label: '$e'); | 
					
						
							| 
									
										
										
										
											2022-09-23 18:28:40 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-11 16:03:04 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | class _DesktopSettingPageState extends State<DesktopSettingPage> | 
					
						
							|  |  |  |     with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |   final List<_TabInfo> settingTabs = <_TabInfo>[ | 
					
						
							|  |  |  |     _TabInfo('General', Icons.settings_outlined, Icons.settings), | 
					
						
							| 
									
										
										
										
											2022-08-23 19:49:11 +08:00
										 |  |  |     _TabInfo('Security', Icons.enhanced_encryption_outlined, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |         Icons.enhanced_encryption), | 
					
						
							|  |  |  |     _TabInfo('Network', Icons.link_outlined, Icons.link), | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  |     _TabInfo('Account', Icons.person_outline, Icons.person), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |     _TabInfo('About', Icons.info_outline, Icons.info) | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   late PageController controller; | 
					
						
							| 
									
										
										
										
											2022-09-23 18:28:40 +08:00
										 |  |  |   late RxInt selectedIndex; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool get wantKeepAlive => true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							| 
									
										
										
										
											2022-09-23 18:28:40 +08:00
										 |  |  |     selectedIndex = (widget.initialPage < 5 ? widget.initialPage : 0).obs; | 
					
						
							|  |  |  |     Get.put<RxInt>(selectedIndex, tag: _kSettingPageIndexTag); | 
					
						
							|  |  |  |     controller = PageController(initialPage: widget.initialPage); | 
					
						
							|  |  |  |     Get.put<PageController>(controller, tag: _kSettingPageControllerTag); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |     Get.delete<PageController>(tag: _kSettingPageControllerTag); | 
					
						
							|  |  |  |     Get.delete<RxInt>(tag: _kSettingPageIndexTag); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     super.build(context); | 
					
						
							|  |  |  |     return Scaffold( | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |       backgroundColor: Theme.of(context).backgroundColor, | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |       body: Row( | 
					
						
							|  |  |  |         children: <Widget>[ | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |           SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |             width: _kTabWidth, | 
					
						
							|  |  |  |             child: Column( | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 _header(), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |                 Flexible(child: _listView(tabs: settingTabs)), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |           ), | 
					
						
							|  |  |  |           const VerticalDivider(thickness: 1, width: 1), | 
					
						
							|  |  |  |           Expanded( | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  |             child: Container( | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |               color: Theme.of(context).scaffoldBackgroundColor, | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |               child: DesktopScrollWrapper( | 
					
						
							|  |  |  |                   scrollController: controller, | 
					
						
							|  |  |  |                   child: PageView( | 
					
						
							|  |  |  |                     controller: controller, | 
					
						
							| 
									
										
										
										
											2022-12-24 14:29:32 +08:00
										 |  |  |                     physics: NeverScrollableScrollPhysics(), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                     children: const [ | 
					
						
							|  |  |  |                       _General(), | 
					
						
							|  |  |  |                       _Safety(), | 
					
						
							|  |  |  |                       _Network(), | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  |                       _Account(), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                       _About(), | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                   )), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |             ), | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   Widget _header() { | 
					
						
							|  |  |  |     return Row( | 
					
						
							|  |  |  |       children: [ | 
					
						
							|  |  |  |         SizedBox( | 
					
						
							|  |  |  |           height: 62, | 
					
						
							|  |  |  |           child: Text( | 
					
						
							|  |  |  |             translate('Settings'), | 
					
						
							|  |  |  |             textAlign: TextAlign.left, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |             style: const TextStyle( | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |               color: _accentColor, | 
					
						
							|  |  |  |               fontSize: _kTitleFontSize, | 
					
						
							|  |  |  |               fontWeight: FontWeight.w400, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ).marginOnly(left: 20, top: 10), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |         const Spacer(), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |       ], | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Widget _listView({required List<_TabInfo> tabs}) { | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |     final scrollController = ScrollController(); | 
					
						
							|  |  |  |     return DesktopScrollWrapper( | 
					
						
							|  |  |  |         scrollController: scrollController, | 
					
						
							|  |  |  |         child: ListView( | 
					
						
							|  |  |  |           physics: NeverScrollableScrollPhysics(), | 
					
						
							|  |  |  |           controller: scrollController, | 
					
						
							|  |  |  |           children: tabs | 
					
						
							|  |  |  |               .asMap() | 
					
						
							|  |  |  |               .entries | 
					
						
							|  |  |  |               .map((tab) => _listItem(tab: tab.value, index: tab.key)) | 
					
						
							|  |  |  |               .toList(), | 
					
						
							|  |  |  |         )); | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-23 19:49:11 +08:00
										 |  |  |   Widget _listItem({required _TabInfo tab, required int index}) { | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |     return Obx(() { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |       bool selected = index == selectedIndex.value; | 
					
						
							|  |  |  |       return SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |         width: _kTabWidth, | 
					
						
							|  |  |  |         height: _kTabHeight, | 
					
						
							|  |  |  |         child: InkWell( | 
					
						
							|  |  |  |           onTap: () { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |             if (selectedIndex.value != index) { | 
					
						
							| 
									
										
										
										
											2022-08-23 19:49:11 +08:00
										 |  |  |               controller.jumpToPage(index); | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |             selectedIndex.value = index; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |           }, | 
					
						
							|  |  |  |           child: Row(children: [ | 
					
						
							|  |  |  |             Container( | 
					
						
							|  |  |  |               width: 4, | 
					
						
							|  |  |  |               height: _kTabHeight * 0.7, | 
					
						
							|  |  |  |               color: selected ? _accentColor : null, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Icon( | 
					
						
							|  |  |  |               selected ? tab.selected : tab.unselected, | 
					
						
							|  |  |  |               color: selected ? _accentColor : null, | 
					
						
							|  |  |  |               size: 20, | 
					
						
							|  |  |  |             ).marginOnly(left: 13, right: 10), | 
					
						
							|  |  |  |             Text( | 
					
						
							|  |  |  |               translate(tab.label), | 
					
						
							|  |  |  |               style: TextStyle( | 
					
						
							|  |  |  |                   color: selected ? _accentColor : null, | 
					
						
							|  |  |  |                   fontWeight: FontWeight.w400, | 
					
						
							|  |  |  |                   fontSize: _kContentFontSize), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ]), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#region pages
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | class _General extends StatefulWidget { | 
					
						
							|  |  |  |   const _General({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<_General> createState() => _GeneralState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _GeneralState extends State<_General> { | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |     final scrollController = ScrollController(); | 
					
						
							|  |  |  |     return DesktopScrollWrapper( | 
					
						
							|  |  |  |         scrollController: scrollController, | 
					
						
							|  |  |  |         child: ListView( | 
					
						
							|  |  |  |           physics: NeverScrollableScrollPhysics(), | 
					
						
							|  |  |  |           controller: scrollController, | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             theme(), | 
					
						
							|  |  |  |             hwcodec(), | 
					
						
							|  |  |  |             audio(context), | 
					
						
							| 
									
										
										
										
											2022-09-15 17:31:28 +08:00
										 |  |  |             record(context), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |             _Card(title: 'Language', children: [language()]), | 
					
						
							| 
									
										
										
										
											2022-10-13 21:19:05 +09:00
										 |  |  |             other() | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |           ], | 
					
						
							|  |  |  |         ).marginOnly(bottom: _kListViewBottomMargin)); | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget theme() { | 
					
						
							| 
									
										
										
										
											2022-09-21 23:32:59 +08:00
										 |  |  |     final current = MyTheme.getThemeModePreference().toShortString(); | 
					
						
							|  |  |  |     onChanged(String value) { | 
					
						
							|  |  |  |       MyTheme.changeDarkMode(MyTheme.themeModeFromString(value)); | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |       setState(() {}); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _Card(title: 'Theme', children: [ | 
					
						
							| 
									
										
										
										
											2022-09-21 23:32:59 +08:00
										 |  |  |       _Radio<String>(context, | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |           value: 'light', | 
					
						
							| 
									
										
										
										
											2022-09-21 23:32:59 +08:00
										 |  |  |           groupValue: current, | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |           label: 'Light', | 
					
						
							| 
									
										
										
										
											2022-09-21 23:32:59 +08:00
										 |  |  |           onChanged: onChanged), | 
					
						
							|  |  |  |       _Radio<String>(context, | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |           value: 'dark', | 
					
						
							| 
									
										
										
										
											2022-09-21 23:32:59 +08:00
										 |  |  |           groupValue: current, | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |           label: 'Dark', | 
					
						
							| 
									
										
										
										
											2022-09-21 23:32:59 +08:00
										 |  |  |           onChanged: onChanged), | 
					
						
							|  |  |  |       _Radio<String>(context, | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |           value: 'system', | 
					
						
							| 
									
										
										
										
											2022-09-21 23:32:59 +08:00
										 |  |  |           groupValue: current, | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |           label: 'Follow System', | 
					
						
							| 
									
										
										
										
											2022-09-21 23:32:59 +08:00
										 |  |  |           onChanged: onChanged), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |     ]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 21:19:05 +09:00
										 |  |  |   Widget other() { | 
					
						
							|  |  |  |     return _Card(title: 'Other', children: [ | 
					
						
							|  |  |  |       _OptionCheckBox(context, 'Confirm before closing multiple tabs', | 
					
						
							|  |  |  |           'enable-confirm-closing-tabs'), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |       _OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'), | 
					
						
							| 
									
										
										
										
											2023-01-05 14:27:28 +08:00
										 |  |  |       if (Platform.isLinux) | 
					
						
							|  |  |  |         Tooltip( | 
					
						
							|  |  |  |           message: translate('software_render_tip'), | 
					
						
							|  |  |  |           child: _OptionCheckBox( | 
					
						
							|  |  |  |             context, | 
					
						
							|  |  |  |             "Always use software rendering", | 
					
						
							|  |  |  |             'allow-always-software-render', | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |     ]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget hwcodec() { | 
					
						
							| 
									
										
										
										
											2022-09-16 19:43:28 +08:00
										 |  |  |     return Offstage( | 
					
						
							|  |  |  |       offstage: !bind.mainHasHwcodec(), | 
					
						
							|  |  |  |       child: _Card(title: 'Hardware Codec', children: [ | 
					
						
							|  |  |  |         _OptionCheckBox(context, 'Enable hardware codec', 'enable-hwcodec'), | 
					
						
							|  |  |  |       ]), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget audio(BuildContext context) { | 
					
						
							|  |  |  |     String getDefault() { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |       if (Platform.isWindows) return 'System Sound'; | 
					
						
							|  |  |  |       return ''; | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Future<String> getValue() async { | 
					
						
							|  |  |  |       String device = await bind.mainGetOption(key: 'audio-input'); | 
					
						
							|  |  |  |       if (device.isNotEmpty) { | 
					
						
							|  |  |  |         return device; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         return getDefault(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setDevice(String device) { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |       if (device == getDefault()) device = ''; | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |       bind.mainSetOption(key: 'audio-input', value: device); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _futureBuilder(future: () async { | 
					
						
							|  |  |  |       List<String> devices = (await bind.mainGetSoundInputs()).toList(); | 
					
						
							|  |  |  |       if (Platform.isWindows) { | 
					
						
							|  |  |  |         devices.insert(0, 'System Sound'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       String current = await getValue(); | 
					
						
							|  |  |  |       return {'devices': devices, 'current': current}; | 
					
						
							|  |  |  |     }(), hasData: (data) { | 
					
						
							|  |  |  |       String currentDevice = data['current']; | 
					
						
							|  |  |  |       List<String> devices = data['devices'] as List<String>; | 
					
						
							|  |  |  |       if (devices.isEmpty) { | 
					
						
							|  |  |  |         return const Offstage(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return _Card(title: 'Audio Input Device', children: [ | 
					
						
							| 
									
										
										
										
											2022-09-13 19:38:50 +08:00
										 |  |  |         ...devices.map((device) => _Radio<String>(context, | 
					
						
							|  |  |  |                 value: device, | 
					
						
							|  |  |  |                 groupValue: currentDevice, | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |                 autoNewLine: false, | 
					
						
							| 
									
										
										
										
											2022-09-13 19:38:50 +08:00
										 |  |  |                 label: device, onChanged: (value) { | 
					
						
							|  |  |  |               setDevice(value); | 
					
						
							|  |  |  |               setState(() {}); | 
					
						
							|  |  |  |             })) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |       ]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-15 17:31:28 +08:00
										 |  |  |   Widget record(BuildContext context) { | 
					
						
							|  |  |  |     return _futureBuilder(future: () async { | 
					
						
							|  |  |  |       String customDirectory = | 
					
						
							|  |  |  |           await bind.mainGetOption(key: 'video-save-directory'); | 
					
						
							|  |  |  |       String defaultDirectory = await bind.mainDefaultVideoSaveDirectory(); | 
					
						
							|  |  |  |       String dir; | 
					
						
							|  |  |  |       if (customDirectory.isNotEmpty) { | 
					
						
							|  |  |  |         dir = customDirectory; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         dir = defaultDirectory; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-10-12 16:06:15 +08:00
										 |  |  |       // canLaunchUrl blocked on windows portable, user SYSTEM
 | 
					
						
							|  |  |  |       return {'dir': dir, 'canlaunch': true}; | 
					
						
							| 
									
										
										
										
											2022-09-15 17:31:28 +08:00
										 |  |  |     }(), hasData: (data) { | 
					
						
							|  |  |  |       Map<String, dynamic> map = data as Map<String, dynamic>; | 
					
						
							|  |  |  |       String dir = map['dir']!; | 
					
						
							|  |  |  |       bool canlaunch = map['canlaunch']! as bool; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return _Card(title: 'Recording', children: [ | 
					
						
							|  |  |  |         _OptionCheckBox(context, 'Automatically record incoming sessions', | 
					
						
							|  |  |  |             'allow-auto-record-incoming'), | 
					
						
							|  |  |  |         Row( | 
					
						
							|  |  |  |           children: [ | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |             Text('${translate("Directory")}:'), | 
					
						
							| 
									
										
										
										
											2022-09-15 17:31:28 +08:00
										 |  |  |             Expanded( | 
					
						
							|  |  |  |               child: GestureDetector( | 
					
						
							|  |  |  |                   onTap: canlaunch ? () => launchUrl(Uri.file(dir)) : null, | 
					
						
							|  |  |  |                   child: Text( | 
					
						
							|  |  |  |                     dir, | 
					
						
							|  |  |  |                     softWrap: true, | 
					
						
							|  |  |  |                     style: | 
					
						
							|  |  |  |                         const TextStyle(decoration: TextDecoration.underline), | 
					
						
							|  |  |  |                   )).marginOnly(left: 10), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             ElevatedButton( | 
					
						
							|  |  |  |                     onPressed: () async { | 
					
						
							|  |  |  |                       String? selectedDirectory = await FilePicker.platform | 
					
						
							|  |  |  |                           .getDirectoryPath(initialDirectory: dir); | 
					
						
							|  |  |  |                       if (selectedDirectory != null) { | 
					
						
							|  |  |  |                         await bind.mainSetOption( | 
					
						
							|  |  |  |                             key: 'video-save-directory', | 
					
						
							|  |  |  |                             value: selectedDirectory); | 
					
						
							|  |  |  |                         setState(() {}); | 
					
						
							|  |  |  |                       } | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                     child: Text(translate('Change'))) | 
					
						
							|  |  |  |                 .marginOnly(left: 5), | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ).marginOnly(left: _kContentHMargin), | 
					
						
							|  |  |  |       ]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   Widget language() { | 
					
						
							|  |  |  |     return _futureBuilder(future: () async { | 
					
						
							|  |  |  |       String langs = await bind.mainGetLangs(); | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |       String lang = bind.mainGetLocalOption(key: kCommConfKeyLang); | 
					
						
							|  |  |  |       return {'langs': langs, 'lang': lang}; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     }(), hasData: (res) { | 
					
						
							|  |  |  |       Map<String, String> data = res as Map<String, String>; | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |       List<dynamic> langsList = jsonDecode(data['langs']!); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |       Map<String, String> langsMap = {for (var v in langsList) v[0]: v[1]}; | 
					
						
							|  |  |  |       List<String> keys = langsMap.keys.toList(); | 
					
						
							|  |  |  |       List<String> values = langsMap.values.toList(); | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |       keys.insert(0, ''); | 
					
						
							|  |  |  |       values.insert(0, 'Default'); | 
					
						
							|  |  |  |       String currentKey = data['lang']!; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |       if (!keys.contains(currentKey)) { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |         currentKey = ''; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |       return _ComboBox( | 
					
						
							|  |  |  |         keys: keys, | 
					
						
							|  |  |  |         values: values, | 
					
						
							|  |  |  |         initialKey: currentKey, | 
					
						
							|  |  |  |         onChanged: (key) async { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |           await bind.mainSetLocalOption(key: kCommConfKeyLang, value: key); | 
					
						
							| 
									
										
										
										
											2022-10-26 14:39:13 +08:00
										 |  |  |           reloadAllWindows(); | 
					
						
							| 
									
										
										
										
											2022-09-08 08:52:56 +08:00
										 |  |  |           bind.mainChangeLanguage(lang: key); | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |         }, | 
					
						
							|  |  |  |       ).marginOnly(left: _kContentHMargin); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 14:35:44 +08:00
										 |  |  | enum _AccessMode { | 
					
						
							|  |  |  |   custom, | 
					
						
							|  |  |  |   full, | 
					
						
							|  |  |  |   view, | 
					
						
							|  |  |  |   deny, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | class _Safety extends StatefulWidget { | 
					
						
							|  |  |  |   const _Safety({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<_Safety> createState() => _SafetyState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool get wantKeepAlive => true; | 
					
						
							| 
									
										
										
										
											2022-09-22 16:18:06 +08:00
										 |  |  |   bool locked = bind.mainIsInstalled(); | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |   final scrollController = ScrollController(); | 
					
						
							| 
									
										
										
										
											2022-11-29 22:00:27 +08:00
										 |  |  |   final RxBool serviceStop = Get.find<RxBool>(tag: 'stop-service'); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     super.build(context); | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |     return DesktopScrollWrapper( | 
					
						
							|  |  |  |         scrollController: scrollController, | 
					
						
							|  |  |  |         child: SingleChildScrollView( | 
					
						
							|  |  |  |             physics: NeverScrollableScrollPhysics(), | 
					
						
							|  |  |  |             controller: scrollController, | 
					
						
							|  |  |  |             child: Column( | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 _lock(locked, 'Unlock Security Settings', () { | 
					
						
							|  |  |  |                   locked = false; | 
					
						
							|  |  |  |                   setState(() => {}); | 
					
						
							|  |  |  |                 }), | 
					
						
							|  |  |  |                 AbsorbPointer( | 
					
						
							|  |  |  |                   absorbing: locked, | 
					
						
							|  |  |  |                   child: Column(children: [ | 
					
						
							|  |  |  |                     permissions(context), | 
					
						
							|  |  |  |                     password(context), | 
					
						
							|  |  |  |                     _Card(title: 'ID', children: [changeId()]), | 
					
						
							| 
									
										
										
										
											2022-09-21 19:18:40 +08:00
										 |  |  |                     more(context), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                   ]), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             )).marginOnly(bottom: _kListViewBottomMargin)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget changeId() { | 
					
						
							|  |  |  |     return _Button('Change ID', changeIdDialog, enabled: !locked); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  |   Widget permissions(context) { | 
					
						
							| 
									
										
										
										
											2022-10-20 09:21:02 +08:00
										 |  |  |     return Obx(() => _permissions(context, serviceStop.value)); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-10-17 14:35:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 09:21:02 +08:00
										 |  |  |   Widget _permissions(context, bool stopService) { | 
					
						
							|  |  |  |     bool enabled = !locked; | 
					
						
							| 
									
										
										
										
											2022-10-17 14:35:44 +08:00
										 |  |  |     return _futureBuilder(future: () async { | 
					
						
							| 
									
										
										
										
											2022-10-20 09:21:02 +08:00
										 |  |  |       return await bind.mainGetOption(key: 'access-mode'); | 
					
						
							| 
									
										
										
										
											2022-10-17 14:35:44 +08:00
										 |  |  |     }(), hasData: (data) { | 
					
						
							| 
									
										
										
										
											2022-10-20 09:21:02 +08:00
										 |  |  |       String accessMode = data! as String; | 
					
						
							| 
									
										
										
										
											2022-10-17 14:35:44 +08:00
										 |  |  |       _AccessMode mode; | 
					
						
							|  |  |  |       if (stopService) { | 
					
						
							|  |  |  |         mode = _AccessMode.deny; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         if (accessMode == 'full') { | 
					
						
							|  |  |  |           mode = _AccessMode.full; | 
					
						
							|  |  |  |         } else if (accessMode == 'view') { | 
					
						
							|  |  |  |           mode = _AccessMode.view; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           mode = _AccessMode.custom; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       String initialKey; | 
					
						
							|  |  |  |       bool? fakeValue; | 
					
						
							|  |  |  |       switch (mode) { | 
					
						
							|  |  |  |         case _AccessMode.custom: | 
					
						
							|  |  |  |           initialKey = ''; | 
					
						
							|  |  |  |           fakeValue = null; | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         case _AccessMode.full: | 
					
						
							|  |  |  |           initialKey = 'full'; | 
					
						
							|  |  |  |           fakeValue = true; | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         case _AccessMode.view: | 
					
						
							|  |  |  |           initialKey = 'view'; | 
					
						
							|  |  |  |           fakeValue = false; | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         case _AccessMode.deny: | 
					
						
							|  |  |  |           initialKey = 'deny'; | 
					
						
							|  |  |  |           fakeValue = false; | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return _Card(title: 'Permissions', children: [ | 
					
						
							|  |  |  |         _ComboBox( | 
					
						
							|  |  |  |             keys: [ | 
					
						
							|  |  |  |               '', | 
					
						
							|  |  |  |               'full', | 
					
						
							|  |  |  |               'view', | 
					
						
							|  |  |  |               'deny' | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |             values: [ | 
					
						
							|  |  |  |               translate('Custom'), | 
					
						
							|  |  |  |               translate('Full Access'), | 
					
						
							|  |  |  |               translate('Screen Share'), | 
					
						
							|  |  |  |               translate('Deny remote access'), | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |             initialKey: initialKey, | 
					
						
							|  |  |  |             onChanged: (mode) async { | 
					
						
							|  |  |  |               String modeValue; | 
					
						
							|  |  |  |               bool stopService; | 
					
						
							|  |  |  |               if (mode == 'deny') { | 
					
						
							|  |  |  |                 modeValue = ''; | 
					
						
							|  |  |  |                 stopService = true; | 
					
						
							|  |  |  |               } else { | 
					
						
							|  |  |  |                 modeValue = mode; | 
					
						
							|  |  |  |                 stopService = false; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               await bind.mainSetOption(key: 'access-mode', value: modeValue); | 
					
						
							|  |  |  |               await bind.mainSetOption( | 
					
						
							|  |  |  |                   key: 'stop-service', | 
					
						
							|  |  |  |                   value: bool2option('stop-service', stopService)); | 
					
						
							|  |  |  |               setState(() {}); | 
					
						
							|  |  |  |             }).marginOnly(left: _kContentHMargin), | 
					
						
							|  |  |  |         Offstage( | 
					
						
							|  |  |  |           offstage: mode == _AccessMode.deny, | 
					
						
							|  |  |  |           child: Column( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               _OptionCheckBox( | 
					
						
							|  |  |  |                   context, 'Enable Keyboard/Mouse', 'enable-keyboard', | 
					
						
							|  |  |  |                   enabled: enabled, fakeValue: fakeValue), | 
					
						
							|  |  |  |               _OptionCheckBox(context, 'Enable Clipboard', 'enable-clipboard', | 
					
						
							|  |  |  |                   enabled: enabled, fakeValue: fakeValue), | 
					
						
							|  |  |  |               _OptionCheckBox( | 
					
						
							|  |  |  |                   context, 'Enable File Transfer', 'enable-file-transfer', | 
					
						
							|  |  |  |                   enabled: enabled, fakeValue: fakeValue), | 
					
						
							|  |  |  |               _OptionCheckBox(context, 'Enable Audio', 'enable-audio', | 
					
						
							|  |  |  |                   enabled: enabled, fakeValue: fakeValue), | 
					
						
							|  |  |  |               _OptionCheckBox(context, 'Enable TCP Tunneling', 'enable-tunnel', | 
					
						
							|  |  |  |                   enabled: enabled, fakeValue: fakeValue), | 
					
						
							|  |  |  |               _OptionCheckBox( | 
					
						
							|  |  |  |                   context, 'Enable Remote Restart', 'enable-remote-restart', | 
					
						
							|  |  |  |                   enabled: enabled, fakeValue: fakeValue), | 
					
						
							|  |  |  |               _OptionCheckBox( | 
					
						
							|  |  |  |                   context, 'Enable Recording Session', 'enable-record-session', | 
					
						
							|  |  |  |                   enabled: enabled, fakeValue: fakeValue), | 
					
						
							|  |  |  |               _OptionCheckBox( | 
					
						
							|  |  |  |                   context, | 
					
						
							|  |  |  |                   'Enable remote configuration modification', | 
					
						
							|  |  |  |                   'allow-remote-config-modification', | 
					
						
							|  |  |  |                   enabled: enabled, | 
					
						
							|  |  |  |                   fakeValue: fakeValue), | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       ]); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  |   Widget password(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     return ChangeNotifierProvider.value( | 
					
						
							|  |  |  |         value: gFFI.serverModel, | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |         child: Consumer<ServerModel>(builder: ((context, model, child) { | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |           List<String> passwordKeys = [ | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |             kUseTemporaryPassword, | 
					
						
							|  |  |  |             kUsePermanentPassword, | 
					
						
							|  |  |  |             kUseBothPasswords, | 
					
						
							|  |  |  |           ]; | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |           List<String> passwordValues = [ | 
					
						
							| 
									
										
										
										
											2022-11-21 14:06:32 +08:00
										 |  |  |             translate('Use one-time password'), | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |             translate('Use permanent password'), | 
					
						
							|  |  |  |             translate('Use both passwords'), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |           ]; | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |           bool tmpEnabled = model.verificationMethod != kUsePermanentPassword; | 
					
						
							|  |  |  |           bool permEnabled = model.verificationMethod != kUseTemporaryPassword; | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |           String currentValue = | 
					
						
							|  |  |  |               passwordValues[passwordKeys.indexOf(model.verificationMethod)]; | 
					
						
							|  |  |  |           List<Widget> radios = passwordValues | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |               .map((value) => _Radio<String>( | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  |                     context, | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |                     value: value, | 
					
						
							|  |  |  |                     groupValue: currentValue, | 
					
						
							|  |  |  |                     label: value, | 
					
						
							|  |  |  |                     onChanged: ((value) { | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |                       () async { | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |                         await model.setVerificationMethod( | 
					
						
							|  |  |  |                             passwordKeys[passwordValues.indexOf(value)]); | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |                         await model.updatePasswordModel(); | 
					
						
							|  |  |  |                       }(); | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |                     }), | 
					
						
							|  |  |  |                     enabled: !locked, | 
					
						
							|  |  |  |                   )) | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |               .toList(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |           var onChanged = tmpEnabled && !locked | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |               ? (value) { | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |                   if (value != null) { | 
					
						
							|  |  |  |                     () async { | 
					
						
							|  |  |  |                       await model.setTemporaryPasswordLength(value.toString()); | 
					
						
							|  |  |  |                       await model.updatePasswordModel(); | 
					
						
							|  |  |  |                     }(); | 
					
						
							|  |  |  |                   } | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |                 } | 
					
						
							|  |  |  |               : null; | 
					
						
							|  |  |  |           List<Widget> lengthRadios = ['6', '8', '10'] | 
					
						
							|  |  |  |               .map((value) => GestureDetector( | 
					
						
							|  |  |  |                     child: Row( | 
					
						
							|  |  |  |                       children: [ | 
					
						
							|  |  |  |                         Radio( | 
					
						
							|  |  |  |                             value: value, | 
					
						
							|  |  |  |                             groupValue: model.temporaryPasswordLength, | 
					
						
							|  |  |  |                             onChanged: onChanged), | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |                         Text( | 
					
						
							|  |  |  |                           value, | 
					
						
							|  |  |  |                           style: TextStyle( | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  |                               color: _disabledTextColor( | 
					
						
							|  |  |  |                                   context, onChanged != null)), | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |                         ), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |                       ], | 
					
						
							|  |  |  |                     ).paddingSymmetric(horizontal: 10), | 
					
						
							|  |  |  |                     onTap: () => onChanged?.call(value), | 
					
						
							|  |  |  |                   )) | 
					
						
							|  |  |  |               .toList(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |           final modeKeys = ['password', 'click', '']; | 
					
						
							|  |  |  |           final modeValues = [ | 
					
						
							|  |  |  |             translate('Accept sessions via password'), | 
					
						
							|  |  |  |             translate('Accept sessions via click'), | 
					
						
							|  |  |  |             translate('Accept sessions via both'), | 
					
						
							|  |  |  |           ]; | 
					
						
							|  |  |  |           var modeInitialKey = model.approveMode; | 
					
						
							|  |  |  |           if (!modeKeys.contains(modeInitialKey)) modeInitialKey = ''; | 
					
						
							|  |  |  |           final usePassword = model.approveMode != 'click'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |           return _Card(title: 'Password', children: [ | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |             _ComboBox( | 
					
						
							|  |  |  |               keys: modeKeys, | 
					
						
							|  |  |  |               values: modeValues, | 
					
						
							|  |  |  |               initialKey: modeInitialKey, | 
					
						
							|  |  |  |               onChanged: (key) => model.setApproveMode(key), | 
					
						
							|  |  |  |             ).marginOnly(left: _kContentHMargin), | 
					
						
							| 
									
										
										
										
											2022-11-23 09:41:05 +08:00
										 |  |  |             if (usePassword) radios[0], | 
					
						
							|  |  |  |             if (usePassword) | 
					
						
							|  |  |  |               _SubLabeledWidget( | 
					
						
							| 
									
										
										
										
											2022-11-21 14:06:32 +08:00
										 |  |  |                   'One-time password length', | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |                   Row( | 
					
						
							|  |  |  |                     children: [ | 
					
						
							|  |  |  |                       ...lengthRadios, | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                   enabled: tmpEnabled && !locked), | 
					
						
							| 
									
										
										
										
											2022-11-23 09:41:05 +08:00
										 |  |  |             if (usePassword) radios[1], | 
					
						
							|  |  |  |             if (usePassword) | 
					
						
							|  |  |  |               _SubButton('Set permanent password', setPasswordDialog, | 
					
						
							| 
									
										
										
										
											2022-11-20 15:53:08 +08:00
										 |  |  |                   permEnabled && !locked), | 
					
						
							| 
									
										
										
										
											2022-11-23 09:41:05 +08:00
										 |  |  |             if (usePassword) | 
					
						
							|  |  |  |               hide_cm(!locked).marginOnly(left: _kContentHSubMargin - 6), | 
					
						
							|  |  |  |             if (usePassword) radios[2], | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |           ]); | 
					
						
							|  |  |  |         }))); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-21 19:18:40 +08:00
										 |  |  |   Widget more(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |     bool enabled = !locked; | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |     return _Card(title: 'Security', children: [ | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |       Offstage( | 
					
						
							|  |  |  |         offstage: !Platform.isWindows, | 
					
						
							|  |  |  |         child: _OptionCheckBox(context, 'Enable RDP', 'enable-rdp', | 
					
						
							|  |  |  |             enabled: enabled), | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2022-09-24 09:58:57 +08:00
										 |  |  |       _OptionCheckBox(context, 'Deny LAN Discovery', 'enable-lan-discovery', | 
					
						
							|  |  |  |           reverse: true, enabled: enabled), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |       ...directIp(context), | 
					
						
							|  |  |  |       whitelist(), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     ]); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   List<Widget> directIp(BuildContext context) { | 
					
						
							|  |  |  |     TextEditingController controller = TextEditingController(); | 
					
						
							|  |  |  |     update() => setState(() {}); | 
					
						
							|  |  |  |     RxBool applyEnabled = false.obs; | 
					
						
							|  |  |  |     return [ | 
					
						
							|  |  |  |       _OptionCheckBox(context, 'Enable Direct IP Access', 'direct-server', | 
					
						
							|  |  |  |           update: update, enabled: !locked), | 
					
						
							|  |  |  |       _futureBuilder( | 
					
						
							|  |  |  |         future: () async { | 
					
						
							|  |  |  |           String enabled = await bind.mainGetOption(key: 'direct-server'); | 
					
						
							|  |  |  |           String port = await bind.mainGetOption(key: 'direct-access-port'); | 
					
						
							|  |  |  |           return {'enabled': enabled, 'port': port}; | 
					
						
							|  |  |  |         }(), | 
					
						
							|  |  |  |         hasData: (data) { | 
					
						
							|  |  |  |           bool enabled = | 
					
						
							|  |  |  |               option2bool('direct-server', data['enabled'].toString()); | 
					
						
							|  |  |  |           if (!enabled) applyEnabled.value = false; | 
					
						
							|  |  |  |           controller.text = data['port'].toString(); | 
					
						
							|  |  |  |           return Offstage( | 
					
						
							|  |  |  |             offstage: !enabled, | 
					
						
							|  |  |  |             child: Row(children: [ | 
					
						
							|  |  |  |               _SubLabeledWidget( | 
					
						
							|  |  |  |                 'Port', | 
					
						
							|  |  |  |                 SizedBox( | 
					
						
							|  |  |  |                   width: 80, | 
					
						
							|  |  |  |                   child: TextField( | 
					
						
							|  |  |  |                     controller: controller, | 
					
						
							|  |  |  |                     enabled: enabled && !locked, | 
					
						
							|  |  |  |                     onChanged: (_) => applyEnabled.value = true, | 
					
						
							|  |  |  |                     inputFormatters: [ | 
					
						
							|  |  |  |                       FilteringTextInputFormatter.allow(RegExp( | 
					
						
							|  |  |  |                           r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')), | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                     textAlign: TextAlign.end, | 
					
						
							|  |  |  |                     decoration: const InputDecoration( | 
					
						
							|  |  |  |                       hintText: '21118', | 
					
						
							|  |  |  |                       border: InputBorder.none, | 
					
						
							|  |  |  |                       contentPadding: EdgeInsets.only(right: 5), | 
					
						
							|  |  |  |                       isCollapsed: true, | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 enabled: enabled && !locked, | 
					
						
							|  |  |  |               ).marginOnly(left: 5), | 
					
						
							|  |  |  |               Obx(() => ElevatedButton( | 
					
						
							|  |  |  |                     onPressed: applyEnabled.value && enabled && !locked | 
					
						
							|  |  |  |                         ? () async { | 
					
						
							|  |  |  |                             applyEnabled.value = false; | 
					
						
							|  |  |  |                             await bind.mainSetOption( | 
					
						
							|  |  |  |                                 key: 'direct-access-port', | 
					
						
							|  |  |  |                                 value: controller.text); | 
					
						
							|  |  |  |                           } | 
					
						
							|  |  |  |                         : null, | 
					
						
							|  |  |  |                     child: Text( | 
					
						
							|  |  |  |                       translate('Apply'), | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                   ).marginOnly(left: 20)) | 
					
						
							|  |  |  |             ]), | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget whitelist() { | 
					
						
							|  |  |  |     bool enabled = !locked; | 
					
						
							|  |  |  |     return _futureBuilder(future: () async { | 
					
						
							|  |  |  |       return await bind.mainGetOption(key: 'whitelist'); | 
					
						
							|  |  |  |     }(), hasData: (data) { | 
					
						
							|  |  |  |       RxBool hasWhitelist = (data as String).isNotEmpty.obs; | 
					
						
							|  |  |  |       update() async { | 
					
						
							|  |  |  |         hasWhitelist.value = | 
					
						
							|  |  |  |             (await bind.mainGetOption(key: 'whitelist')).isNotEmpty; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       onChanged(bool? checked) async { | 
					
						
							|  |  |  |         changeWhiteList(callback: update); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return GestureDetector( | 
					
						
							|  |  |  |         child: Tooltip( | 
					
						
							|  |  |  |           message: translate('whitelist_tip'), | 
					
						
							|  |  |  |           child: Obx(() => Row( | 
					
						
							|  |  |  |                 children: [ | 
					
						
							|  |  |  |                   Checkbox( | 
					
						
							|  |  |  |                           value: hasWhitelist.value, | 
					
						
							|  |  |  |                           onChanged: enabled ? onChanged : null) | 
					
						
							|  |  |  |                       .marginOnly(right: 5), | 
					
						
							|  |  |  |                   Offstage( | 
					
						
							|  |  |  |                     offstage: !hasWhitelist.value, | 
					
						
							| 
									
										
										
										
											2022-09-21 19:18:40 +08:00
										 |  |  |                     child: const Icon(Icons.warning_amber_rounded, | 
					
						
							|  |  |  |                             color: Color.fromARGB(255, 255, 204, 0)) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |                         .marginOnly(right: 5), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                   Expanded( | 
					
						
							|  |  |  |                       child: Text( | 
					
						
							|  |  |  |                     translate('Use IP Whitelisting'), | 
					
						
							|  |  |  |                     style: | 
					
						
							|  |  |  |                         TextStyle(color: _disabledTextColor(context, enabled)), | 
					
						
							|  |  |  |                   )) | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |               )), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         onTap: () { | 
					
						
							|  |  |  |           onChanged(!hasWhitelist.value); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ).marginOnly(left: _kCheckBoxLeftMargin); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-11-23 09:41:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Widget hide_cm(bool enabled) { | 
					
						
							|  |  |  |     return ChangeNotifierProvider.value( | 
					
						
							|  |  |  |         value: gFFI.serverModel, | 
					
						
							|  |  |  |         child: Consumer<ServerModel>(builder: (context, model, child) { | 
					
						
							|  |  |  |           final enableHideCm = model.approveMode == 'password' && | 
					
						
							|  |  |  |               model.verificationMethod == kUsePermanentPassword; | 
					
						
							|  |  |  |           onHideCmChanged(bool? b) { | 
					
						
							|  |  |  |             if (b != null) { | 
					
						
							|  |  |  |               bind.mainSetOption( | 
					
						
							|  |  |  |                   key: 'allow-hide-cm', value: bool2option('allow-hide-cm', b)); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return Tooltip( | 
					
						
							|  |  |  |               message: enableHideCm ? "" : translate('hide_cm_tip'), | 
					
						
							|  |  |  |               child: GestureDetector( | 
					
						
							|  |  |  |                 onTap: | 
					
						
							|  |  |  |                     enableHideCm ? () => onHideCmChanged(!model.hideCm) : null, | 
					
						
							|  |  |  |                 child: Row( | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     Checkbox( | 
					
						
							|  |  |  |                             value: model.hideCm, | 
					
						
							|  |  |  |                             onChanged: enabled && enableHideCm | 
					
						
							|  |  |  |                                 ? onHideCmChanged | 
					
						
							|  |  |  |                                 : null) | 
					
						
							|  |  |  |                         .marginOnly(right: 5), | 
					
						
							|  |  |  |                     Expanded( | 
					
						
							|  |  |  |                       child: Text( | 
					
						
							|  |  |  |                         translate('Hide connection management window'), | 
					
						
							|  |  |  |                         style: TextStyle( | 
					
						
							|  |  |  |                             color: _disabledTextColor( | 
					
						
							|  |  |  |                                 context, enabled && enableHideCm)), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                   ], | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               )); | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | class _Network extends StatefulWidget { | 
					
						
							|  |  |  |   const _Network({Key? key}) : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |   State<_Network> createState() => _NetworkState(); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   bool get wantKeepAlive => true; | 
					
						
							| 
									
										
										
										
											2022-10-12 16:06:15 +08:00
										 |  |  |   bool locked = bind.mainIsInstalled(); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     super.build(context); | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |     bool enabled = !locked; | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |     final scrollController = ScrollController(); | 
					
						
							|  |  |  |     return DesktopScrollWrapper( | 
					
						
							|  |  |  |         scrollController: scrollController, | 
					
						
							|  |  |  |         child: ListView( | 
					
						
							|  |  |  |             controller: scrollController, | 
					
						
							|  |  |  |             physics: NeverScrollableScrollPhysics(), | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               _lock(locked, 'Unlock Network Settings', () { | 
					
						
							|  |  |  |                 locked = false; | 
					
						
							|  |  |  |                 setState(() => {}); | 
					
						
							|  |  |  |               }), | 
					
						
							|  |  |  |               AbsorbPointer( | 
					
						
							|  |  |  |                 absorbing: locked, | 
					
						
							|  |  |  |                 child: Column(children: [ | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |                   server(enabled), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                   _Card(title: 'Proxy', children: [ | 
					
						
							|  |  |  |                     _Button('Socks5 Proxy', changeSocks5Proxy, | 
					
						
							|  |  |  |                         enabled: enabled), | 
					
						
							|  |  |  |                   ]), | 
					
						
							|  |  |  |                 ]), | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |             ]).marginOnly(bottom: _kListViewBottomMargin)); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   server(bool enabled) { | 
					
						
							|  |  |  |     return _futureBuilder(future: () async { | 
					
						
							|  |  |  |       return await bind.mainGetOptions(); | 
					
						
							|  |  |  |     }(), hasData: (data) { | 
					
						
							|  |  |  |       // Setting page is not modal, oldOptions should only be used when getting options, never when setting.
 | 
					
						
							|  |  |  |       Map<String, dynamic> oldOptions = jsonDecode(data! as String); | 
					
						
							|  |  |  |       old(String key) { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |         return (oldOptions[key] ?? '').trim(); | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |       RxString idErrMsg = ''.obs; | 
					
						
							|  |  |  |       RxString relayErrMsg = ''.obs; | 
					
						
							|  |  |  |       RxString apiErrMsg = ''.obs; | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |       var idController = | 
					
						
							|  |  |  |           TextEditingController(text: old('custom-rendezvous-server')); | 
					
						
							|  |  |  |       var relayController = TextEditingController(text: old('relay-server')); | 
					
						
							|  |  |  |       var apiController = TextEditingController(text: old('api-server')); | 
					
						
							|  |  |  |       var keyController = TextEditingController(text: old('key')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       set(String idServer, String relayServer, String apiServer, | 
					
						
							|  |  |  |           String key) async { | 
					
						
							|  |  |  |         idServer = idServer.trim(); | 
					
						
							|  |  |  |         relayServer = relayServer.trim(); | 
					
						
							|  |  |  |         apiServer = apiServer.trim(); | 
					
						
							|  |  |  |         key = key.trim(); | 
					
						
							|  |  |  |         if (idServer.isNotEmpty) { | 
					
						
							|  |  |  |           idErrMsg.value = | 
					
						
							|  |  |  |               translate(await bind.mainTestIfValidServer(server: idServer)); | 
					
						
							|  |  |  |           if (idErrMsg.isNotEmpty) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (relayServer.isNotEmpty) { | 
					
						
							|  |  |  |           relayErrMsg.value = | 
					
						
							|  |  |  |               translate(await bind.mainTestIfValidServer(server: relayServer)); | 
					
						
							|  |  |  |           if (relayErrMsg.isNotEmpty) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (apiServer.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2022-11-18 11:56:32 +08:00
										 |  |  |           if (!apiServer.startsWith('http://') && | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |               !apiServer.startsWith('https://')) { | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |             apiErrMsg.value = | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                 '${translate("API Server")}: ${translate("invalid_http")}'; | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |             return false; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-12-16 23:18:30 +09:00
										 |  |  |         final old = await bind.mainGetOption(key: 'custom-rendezvous-server'); | 
					
						
							|  |  |  |         if (old.isNotEmpty && old != idServer) { | 
					
						
							|  |  |  |           await gFFI.userModel.logOut(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |         // should set one by one
 | 
					
						
							|  |  |  |         await bind.mainSetOption( | 
					
						
							|  |  |  |             key: 'custom-rendezvous-server', value: idServer); | 
					
						
							|  |  |  |         await bind.mainSetOption(key: 'relay-server', value: relayServer); | 
					
						
							|  |  |  |         await bind.mainSetOption(key: 'api-server', value: apiServer); | 
					
						
							|  |  |  |         await bind.mainSetOption(key: 'key', value: key); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       submit() async { | 
					
						
							|  |  |  |         bool result = await set(idController.text, relayController.text, | 
					
						
							|  |  |  |             apiController.text, keyController.text); | 
					
						
							|  |  |  |         if (result) { | 
					
						
							|  |  |  |           setState(() {}); | 
					
						
							|  |  |  |           showToast(translate('Successful')); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           showToast(translate('Failed')); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       import() { | 
					
						
							|  |  |  |         Clipboard.getData(Clipboard.kTextPlain).then((value) { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:14:43 +09:00
										 |  |  |           final text = value?.text; | 
					
						
							|  |  |  |           if (text != null && text.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |             try { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:14:43 +09:00
										 |  |  |               final sc = ServerConfig.decode(text); | 
					
						
							|  |  |  |               if (sc.idServer.isNotEmpty) { | 
					
						
							|  |  |  |                 idController.text = sc.idServer; | 
					
						
							|  |  |  |                 relayController.text = sc.relayServer; | 
					
						
							|  |  |  |                 apiController.text = sc.apiServer; | 
					
						
							|  |  |  |                 keyController.text = sc.key; | 
					
						
							|  |  |  |                 Future<bool> success = | 
					
						
							|  |  |  |                     set(sc.idServer, sc.relayServer, sc.apiServer, sc.key); | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |                 success.then((value) { | 
					
						
							|  |  |  |                   if (value) { | 
					
						
							|  |  |  |                     showToast( | 
					
						
							|  |  |  |                         translate('Import server configuration successfully')); | 
					
						
							|  |  |  |                   } else { | 
					
						
							|  |  |  |                     showToast(translate('Invalid server configuration')); | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |               } else { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                 showToast(translate('Invalid server configuration')); | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             } catch (e) { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |               showToast(translate('Invalid server configuration')); | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |             } | 
					
						
							|  |  |  |           } else { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |             showToast(translate('Clipboard is empty')); | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export() { | 
					
						
							| 
									
										
										
										
											2022-12-21 15:14:43 +09:00
										 |  |  |         final text = ServerConfig( | 
					
						
							|  |  |  |                 idServer: idController.text, | 
					
						
							|  |  |  |                 relayServer: relayController.text, | 
					
						
							|  |  |  |                 apiServer: apiController.text, | 
					
						
							|  |  |  |                 key: keyController.text) | 
					
						
							|  |  |  |             .encode(); | 
					
						
							|  |  |  |         debugPrint("ServerConfig export: $text"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Clipboard.setData(ClipboardData(text: text)); | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |         showToast(translate('Export server configuration successfully')); | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       bool secure = !enabled; | 
					
						
							|  |  |  |       return _Card(title: 'ID/Relay Server', title_suffix: [ | 
					
						
							|  |  |  |         Tooltip( | 
					
						
							|  |  |  |           message: translate('Import Server Config'), | 
					
						
							|  |  |  |           child: IconButton( | 
					
						
							|  |  |  |               icon: Icon(Icons.paste, color: Colors.grey), | 
					
						
							|  |  |  |               onPressed: enabled ? import : null), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         Tooltip( | 
					
						
							|  |  |  |             message: translate('Export Server Config'), | 
					
						
							|  |  |  |             child: IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.copy, color: Colors.grey), | 
					
						
							|  |  |  |                 onPressed: enabled ? export : null)), | 
					
						
							|  |  |  |       ], children: [ | 
					
						
							|  |  |  |         Column( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Obx(() => _LabeledTextField(context, 'ID Server', idController, | 
					
						
							|  |  |  |                 idErrMsg.value, enabled, secure)), | 
					
						
							|  |  |  |             Obx(() => _LabeledTextField(context, 'Relay Server', | 
					
						
							|  |  |  |                 relayController, relayErrMsg.value, enabled, secure)), | 
					
						
							|  |  |  |             Obx(() => _LabeledTextField(context, 'API Server', apiController, | 
					
						
							|  |  |  |                 apiErrMsg.value, enabled, secure)), | 
					
						
							|  |  |  |             _LabeledTextField( | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                 context, 'Key', keyController, '', enabled, secure), | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |             Row( | 
					
						
							|  |  |  |               mainAxisAlignment: MainAxisAlignment.end, | 
					
						
							|  |  |  |               children: [_Button('Apply', submit, enabled: enabled)], | 
					
						
							|  |  |  |             ).marginOnly(top: 15), | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       ]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  | class _Account extends StatefulWidget { | 
					
						
							|  |  |  |   const _Account({Key? key}) : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  |   State<_Account> createState() => _AccountState(); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  | class _AccountState extends State<_Account> { | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |     final scrollController = ScrollController(); | 
					
						
							|  |  |  |     return DesktopScrollWrapper( | 
					
						
							|  |  |  |         scrollController: scrollController, | 
					
						
							|  |  |  |         child: ListView( | 
					
						
							|  |  |  |           physics: NeverScrollableScrollPhysics(), | 
					
						
							|  |  |  |           controller: scrollController, | 
					
						
							|  |  |  |           children: [ | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  |             _Card(title: 'Account', children: [accountAction()]), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |           ], | 
					
						
							|  |  |  |         ).marginOnly(bottom: _kListViewBottomMargin)); | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-22 17:38:18 +08:00
										 |  |  |   Widget accountAction() { | 
					
						
							| 
									
										
										
										
											2022-12-11 21:40:35 +08:00
										 |  |  |     return Obx(() => _Button( | 
					
						
							|  |  |  |         gFFI.userModel.userName.value.isEmpty ? 'Login' : 'Logout', | 
					
						
							|  |  |  |         () => { | 
					
						
							|  |  |  |               gFFI.userModel.userName.value.isEmpty | 
					
						
							|  |  |  |                   ? loginDialog() | 
					
						
							|  |  |  |                   : gFFI.userModel.logOut() | 
					
						
							|  |  |  |             })); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _About extends StatefulWidget { | 
					
						
							|  |  |  |   const _About({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<_About> createState() => _AboutState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _AboutState extends State<_About> { | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return _futureBuilder(future: () async { | 
					
						
							|  |  |  |       final license = await bind.mainGetLicense(); | 
					
						
							|  |  |  |       final version = await bind.mainGetVersion(); | 
					
						
							| 
									
										
										
										
											2022-11-17 16:36:07 +08:00
										 |  |  |       final buildDate = await bind.mainGetBuildDate(); | 
					
						
							|  |  |  |       return {'license': license, 'version': version, 'buildDate': buildDate}; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |     }(), hasData: (data) { | 
					
						
							|  |  |  |       final license = data['license'].toString(); | 
					
						
							|  |  |  |       final version = data['version'].toString(); | 
					
						
							| 
									
										
										
										
											2022-11-17 16:36:07 +08:00
										 |  |  |       final buildDate = data['buildDate'].toString(); | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |       const linkStyle = TextStyle(decoration: TextDecoration.underline); | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |       final scrollController = ScrollController(); | 
					
						
							|  |  |  |       return DesktopScrollWrapper( | 
					
						
							|  |  |  |           scrollController: scrollController, | 
					
						
							|  |  |  |           child: SingleChildScrollView( | 
					
						
							|  |  |  |             controller: scrollController, | 
					
						
							|  |  |  |             physics: NeverScrollableScrollPhysics(), | 
					
						
							| 
									
										
										
										
											2022-12-27 23:26:11 +08:00
										 |  |  |             child: _Card(title: '${translate('About')} RustDesk', children: [ | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |               Column( | 
					
						
							|  |  |  |                 crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |                 children: [ | 
					
						
							|  |  |  |                   const SizedBox( | 
					
						
							|  |  |  |                     height: 8.0, | 
					
						
							|  |  |  |                   ), | 
					
						
							| 
									
										
										
										
											2022-12-27 23:26:11 +08:00
										 |  |  |                   Text('${translate('Version')}: $version') | 
					
						
							| 
									
										
										
										
											2022-12-21 15:14:43 +09:00
										 |  |  |                       .marginSymmetric(vertical: 4.0), | 
					
						
							| 
									
										
										
										
											2022-12-27 23:26:11 +08:00
										 |  |  |                   Text('${translate('Build Date')}: $buildDate') | 
					
						
							| 
									
										
										
										
											2022-12-21 15:14:43 +09:00
										 |  |  |                       .marginSymmetric(vertical: 4.0), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                   InkWell( | 
					
						
							|  |  |  |                       onTap: () { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                         launchUrlString('https://rustdesk.com/privacy'); | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                       }, | 
					
						
							| 
									
										
										
										
											2022-12-17 14:40:57 +01:00
										 |  |  |                       child: Text( | 
					
						
							|  |  |  |                         translate('Privacy Statement'), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                         style: linkStyle, | 
					
						
							|  |  |  |                       ).marginSymmetric(vertical: 4.0)), | 
					
						
							|  |  |  |                   InkWell( | 
					
						
							|  |  |  |                       onTap: () { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                         launchUrlString('https://rustdesk.com'); | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                       }, | 
					
						
							| 
									
										
										
										
											2022-12-17 14:40:57 +01:00
										 |  |  |                       child: Text( | 
					
						
							|  |  |  |                         translate('Website'), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                         style: linkStyle, | 
					
						
							|  |  |  |                       ).marginSymmetric(vertical: 4.0)), | 
					
						
							|  |  |  |                   Container( | 
					
						
							|  |  |  |                     decoration: const BoxDecoration(color: Color(0xFF2c8cff)), | 
					
						
							|  |  |  |                     padding: | 
					
						
							|  |  |  |                         const EdgeInsets.symmetric(vertical: 24, horizontal: 8), | 
					
						
							|  |  |  |                     child: Row( | 
					
						
							|  |  |  |                       children: [ | 
					
						
							|  |  |  |                         Expanded( | 
					
						
							|  |  |  |                           child: Column( | 
					
						
							|  |  |  |                             crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |                             children: [ | 
					
						
							|  |  |  |                               Text( | 
					
						
							| 
									
										
										
										
											2022-11-17 08:57:53 +01:00
										 |  |  |                                 'Copyright © 2022 Purslane Ltd.\n$license', | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                                 style: const TextStyle(color: Colors.white), | 
					
						
							|  |  |  |                               ), | 
					
						
							| 
									
										
										
										
											2022-12-17 14:40:57 +01:00
										 |  |  |                               Text( | 
					
						
							|  |  |  |                                 translate('Slogan_tip'), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                                 style: TextStyle( | 
					
						
							|  |  |  |                                     fontWeight: FontWeight.w800, | 
					
						
							|  |  |  |                                     color: Colors.white), | 
					
						
							|  |  |  |                               ) | 
					
						
							|  |  |  |                             ], | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |                           ), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                         ), | 
					
						
							|  |  |  |                       ], | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2022-09-21 18:49:28 +08:00
										 |  |  |                   ).marginSymmetric(vertical: 4.0) | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |               ).marginOnly(left: _kContentHMargin) | 
					
						
							|  |  |  |             ]), | 
					
						
							|  |  |  |           )); | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-08-11 16:03:04 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | //#endregion
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#region components
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | // ignore: non_constant_identifier_names
 | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  | Widget _Card( | 
					
						
							|  |  |  |     {required String title, | 
					
						
							|  |  |  |     required List<Widget> children, | 
					
						
							|  |  |  |     List<Widget>? title_suffix}) { | 
					
						
							| 
									
										
										
										
											2022-10-13 10:23:52 +08:00
										 |  |  |   return Row( | 
					
						
							|  |  |  |     children: [ | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |       Flexible( | 
					
						
							|  |  |  |         child: SizedBox( | 
					
						
							|  |  |  |           width: _kCardFixedWidth, | 
					
						
							|  |  |  |           child: Card( | 
					
						
							|  |  |  |             child: Column( | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 Row( | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     Expanded( | 
					
						
							|  |  |  |                         child: Text( | 
					
						
							|  |  |  |                       translate(title), | 
					
						
							|  |  |  |                       textAlign: TextAlign.start, | 
					
						
							|  |  |  |                       style: const TextStyle( | 
					
						
							|  |  |  |                         fontSize: _kTitleFontSize, | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     )), | 
					
						
							|  |  |  |                     ...?title_suffix | 
					
						
							|  |  |  |                   ], | 
					
						
							|  |  |  |                 ).marginOnly(left: _kContentHMargin, top: 10, bottom: 10), | 
					
						
							| 
									
										
										
										
											2022-10-13 10:23:52 +08:00
										 |  |  |                 ...children | 
					
						
							|  |  |  |                     .map((e) => e.marginOnly(top: 4, right: _kContentHMargin)), | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |               ], | 
					
						
							|  |  |  |             ).marginOnly(bottom: 10), | 
					
						
							|  |  |  |           ).marginOnly(left: _kCardLeftMargin, top: 15), | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2022-10-13 10:23:52 +08:00
										 |  |  |       ), | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  | Color? _disabledTextColor(BuildContext context, bool enabled) { | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |   return enabled | 
					
						
							|  |  |  |       ? null | 
					
						
							|  |  |  |       : Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6); | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | // ignore: non_constant_identifier_names
 | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  | Widget _OptionCheckBox(BuildContext context, String label, String key, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |     {Function()? update, | 
					
						
							|  |  |  |     bool reverse = false, | 
					
						
							|  |  |  |     bool enabled = true, | 
					
						
							| 
									
										
										
										
											2022-10-17 14:35:44 +08:00
										 |  |  |     Icon? checkedIcon, | 
					
						
							|  |  |  |     bool? fakeValue}) { | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   return _futureBuilder( | 
					
						
							|  |  |  |       future: bind.mainGetOption(key: key), | 
					
						
							|  |  |  |       hasData: (data) { | 
					
						
							|  |  |  |         bool value = option2bool(key, data.toString()); | 
					
						
							|  |  |  |         if (reverse) value = !value; | 
					
						
							|  |  |  |         var ref = value.obs; | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |         onChanged(option) async { | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |           if (option != null) { | 
					
						
							|  |  |  |             ref.value = option; | 
					
						
							|  |  |  |             if (reverse) option = !option; | 
					
						
							|  |  |  |             String value = bool2option(key, option); | 
					
						
							| 
									
										
										
										
											2023-01-05 14:27:28 +08:00
										 |  |  |             await bind.mainSetOption(key: key, value: value); | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |             update?.call(); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 14:35:44 +08:00
										 |  |  |         if (fakeValue != null) { | 
					
						
							|  |  |  |           ref.value = fakeValue; | 
					
						
							|  |  |  |           enabled = false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |         return GestureDetector( | 
					
						
							|  |  |  |           child: Obx( | 
					
						
							|  |  |  |             () => Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |                 Checkbox( | 
					
						
							|  |  |  |                         value: ref.value, onChanged: enabled ? onChanged : null) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |                     .marginOnly(right: 5), | 
					
						
							|  |  |  |                 Offstage( | 
					
						
							|  |  |  |                   offstage: !ref.value || checkedIcon == null, | 
					
						
							|  |  |  |                   child: checkedIcon?.marginOnly(right: 5), | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |                 Expanded( | 
					
						
							|  |  |  |                     child: Text( | 
					
						
							|  |  |  |                   translate(label), | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  |                   style: TextStyle(color: _disabledTextColor(context, enabled)), | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |                 )) | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ).marginOnly(left: _kCheckBoxLeftMargin), | 
					
						
							| 
									
										
										
										
											2022-10-16 12:32:52 +08:00
										 |  |  |           onTap: enabled | 
					
						
							|  |  |  |               ? () { | 
					
						
							|  |  |  |                   onChanged(!ref.value); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               : null, | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |         ); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | // ignore: non_constant_identifier_names
 | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  | Widget _Radio<T>(BuildContext context, | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |     {required T value, | 
					
						
							|  |  |  |     required T groupValue, | 
					
						
							|  |  |  |     required String label, | 
					
						
							|  |  |  |     required Function(T value) onChanged, | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |     bool autoNewLine = true, | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |     bool enabled = true}) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |   var onChange = enabled | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |       ? (T? value) { | 
					
						
							|  |  |  |           if (value != null) { | 
					
						
							|  |  |  |             onChanged(value); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       : null; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   return GestureDetector( | 
					
						
							|  |  |  |     child: Row( | 
					
						
							|  |  |  |       children: [ | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |         Radio<T>(value: value, groupValue: groupValue, onChanged: onChange), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |         Expanded( | 
					
						
							|  |  |  |           child: Text(translate(label), | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |                   overflow: autoNewLine ? null : TextOverflow.ellipsis, | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |                   style: TextStyle( | 
					
						
							|  |  |  |                       fontSize: _kContentFontSize, | 
					
						
							| 
									
										
										
										
											2022-08-20 19:57:16 +08:00
										 |  |  |                       color: _disabledTextColor(context, enabled))) | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |               .marginOnly(left: 5), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |     ).marginOnly(left: _kRadioLeftMargin), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |     onTap: () => onChange?.call(value), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | // ignore: non_constant_identifier_names
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | Widget _Button(String label, Function() onPressed, | 
					
						
							|  |  |  |     {bool enabled = true, String? tip}) { | 
					
						
							|  |  |  |   var button = ElevatedButton( | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |     onPressed: enabled ? onPressed : null, | 
					
						
							|  |  |  |     child: Text( | 
					
						
							|  |  |  |       translate(label), | 
					
						
							|  |  |  |     ).marginSymmetric(horizontal: 15), | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |   StatefulWidget child; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   if (tip == null) { | 
					
						
							|  |  |  |     child = button; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     child = Tooltip(message: translate(tip), child: button); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   return Row(children: [ | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |     child, | 
					
						
							|  |  |  |   ]).marginOnly(left: _kContentHMargin); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | // ignore: non_constant_identifier_names
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | Widget _SubButton(String label, Function() onPressed, [bool enabled = true]) { | 
					
						
							|  |  |  |   return Row( | 
					
						
							|  |  |  |     children: [ | 
					
						
							|  |  |  |       ElevatedButton( | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |         onPressed: enabled ? onPressed : null, | 
					
						
							|  |  |  |         child: Text( | 
					
						
							|  |  |  |           translate(label), | 
					
						
							|  |  |  |         ).marginSymmetric(horizontal: 15), | 
					
						
							|  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |     ], | 
					
						
							|  |  |  |   ).marginOnly(left: _kContentHSubMargin); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  | // ignore: non_constant_identifier_names
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | Widget _SubLabeledWidget(String label, Widget child, {bool enabled = true}) { | 
					
						
							|  |  |  |   RxBool hover = false.obs; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   return Row( | 
					
						
							|  |  |  |     children: [ | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |       MouseRegion( | 
					
						
							|  |  |  |           onEnter: (_) => hover.value = true, | 
					
						
							|  |  |  |           onExit: (_) => hover.value = false, | 
					
						
							|  |  |  |           child: Obx( | 
					
						
							|  |  |  |             () { | 
					
						
							|  |  |  |               return Container( | 
					
						
							|  |  |  |                   height: 32, | 
					
						
							|  |  |  |                   decoration: BoxDecoration( | 
					
						
							|  |  |  |                       border: Border.all( | 
					
						
							|  |  |  |                           color: hover.value && enabled | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |                               ? const Color(0xFFD7D7D7) | 
					
						
							|  |  |  |                               : const Color(0xFFCBCBCB), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |                           width: hover.value && enabled ? 2 : 1)), | 
					
						
							|  |  |  |                   child: Row( | 
					
						
							|  |  |  |                     children: [ | 
					
						
							|  |  |  |                       Container( | 
					
						
							|  |  |  |                         height: 28, | 
					
						
							|  |  |  |                         color: (hover.value && enabled) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |                             ? const Color(0xFFD7D7D7) | 
					
						
							|  |  |  |                             : const Color(0xFFCBCBCB), | 
					
						
							|  |  |  |                         alignment: Alignment.center, | 
					
						
							|  |  |  |                         padding: const EdgeInsets.symmetric( | 
					
						
							|  |  |  |                             horizontal: 5, vertical: 2), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |                         child: Text( | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |                           '${translate(label)}: ', | 
					
						
							|  |  |  |                           style: const TextStyle(fontWeight: FontWeight.w300), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |                         ), | 
					
						
							|  |  |  |                       ).paddingAll(2), | 
					
						
							|  |  |  |                       child, | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                   )); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           )), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     ], | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   ).marginOnly(left: _kContentHSubMargin); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Widget _futureBuilder( | 
					
						
							|  |  |  |     {required Future? future, required Widget Function(dynamic data) hasData}) { | 
					
						
							|  |  |  |   return FutureBuilder( | 
					
						
							|  |  |  |       future: future, | 
					
						
							|  |  |  |       builder: (BuildContext context, AsyncSnapshot snapshot) { | 
					
						
							|  |  |  |         if (snapshot.hasData) { | 
					
						
							|  |  |  |           return hasData(snapshot.data!); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           if (snapshot.hasError) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |             debugPrint(snapshot.error.toString()); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |           return Container(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  | Widget _lock( | 
					
						
							|  |  |  |   bool locked, | 
					
						
							|  |  |  |   String label, | 
					
						
							|  |  |  |   Function() onUnlock, | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   return Offstage( | 
					
						
							|  |  |  |       offstage: !locked, | 
					
						
							|  |  |  |       child: Row( | 
					
						
							|  |  |  |         children: [ | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |           Flexible( | 
					
						
							|  |  |  |             child: SizedBox( | 
					
						
							|  |  |  |               width: _kCardFixedWidth, | 
					
						
							|  |  |  |               child: Card( | 
					
						
							|  |  |  |                 child: ElevatedButton( | 
					
						
							|  |  |  |                   child: SizedBox( | 
					
						
							|  |  |  |                       height: 25, | 
					
						
							|  |  |  |                       child: Row( | 
					
						
							|  |  |  |                           mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |                           children: [ | 
					
						
							|  |  |  |                             const Icon( | 
					
						
							|  |  |  |                               Icons.security_sharp, | 
					
						
							|  |  |  |                               size: 20, | 
					
						
							|  |  |  |                             ), | 
					
						
							|  |  |  |                             Text(translate(label)).marginOnly(left: 5), | 
					
						
							|  |  |  |                           ]).marginSymmetric(vertical: 2)), | 
					
						
							|  |  |  |                   onPressed: () async { | 
					
						
							|  |  |  |                     bool checked = await bind.mainCheckSuperUserPermission(); | 
					
						
							|  |  |  |                     if (checked) { | 
					
						
							|  |  |  |                       onUnlock(); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   }, | 
					
						
							|  |  |  |                 ).marginSymmetric(horizontal: 2, vertical: 4), | 
					
						
							|  |  |  |               ).marginOnly(left: _kCardLeftMargin), | 
					
						
							|  |  |  |             ).marginOnly(top: 10), | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2022-08-19 15:44:19 +08:00
										 |  |  |         ], | 
					
						
							|  |  |  |       )); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  | _LabeledTextField( | 
					
						
							|  |  |  |     BuildContext context, | 
					
						
							| 
									
										
										
										
											2023-01-09 02:30:29 -05:00
										 |  |  |     String label, | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |     TextEditingController controller, | 
					
						
							|  |  |  |     String errorText, | 
					
						
							|  |  |  |     bool enabled, | 
					
						
							|  |  |  |     bool secure) { | 
					
						
							|  |  |  |   return Row( | 
					
						
							|  |  |  |     children: [ | 
					
						
							|  |  |  |       Spacer(flex: 1), | 
					
						
							|  |  |  |       Expanded( | 
					
						
							|  |  |  |         flex: 4, | 
					
						
							|  |  |  |         child: Text( | 
					
						
							| 
									
										
										
										
											2023-01-09 02:30:29 -05:00
										 |  |  |           '${translate(label)}:', | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |           textAlign: TextAlign.right, | 
					
						
							|  |  |  |           style: TextStyle(color: _disabledTextColor(context, enabled)), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       Spacer(flex: 1), | 
					
						
							|  |  |  |       Expanded( | 
					
						
							|  |  |  |         flex: 10, | 
					
						
							|  |  |  |         child: TextField( | 
					
						
							|  |  |  |             controller: controller, | 
					
						
							|  |  |  |             enabled: enabled, | 
					
						
							|  |  |  |             obscureText: secure, | 
					
						
							|  |  |  |             decoration: InputDecoration( | 
					
						
							| 
									
										
										
										
											2023-01-09 19:28:56 +08:00
										 |  |  |                 isDense: true, | 
					
						
							|  |  |  |                 contentPadding: EdgeInsets.symmetric(vertical: 15), | 
					
						
							| 
									
										
										
										
											2022-10-22 12:03:57 +08:00
										 |  |  |                 errorText: errorText.isNotEmpty ? errorText : null), | 
					
						
							|  |  |  |             style: TextStyle( | 
					
						
							|  |  |  |               color: _disabledTextColor(context, enabled), | 
					
						
							|  |  |  |             )), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       Spacer(flex: 1), | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  | // ignore: must_be_immutable
 | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | class _ComboBox extends StatelessWidget { | 
					
						
							|  |  |  |   late final List<String> keys; | 
					
						
							|  |  |  |   late final List<String> values; | 
					
						
							|  |  |  |   late final String initialKey; | 
					
						
							|  |  |  |   late final Function(String key) onChanged; | 
					
						
							|  |  |  |   late final bool enabled; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |   late String current; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   _ComboBox({ | 
					
						
							|  |  |  |     Key? key, | 
					
						
							|  |  |  |     required this.keys, | 
					
						
							|  |  |  |     required this.values, | 
					
						
							|  |  |  |     required this.initialKey, | 
					
						
							|  |  |  |     required this.onChanged, | 
					
						
							| 
									
										
										
										
											2022-09-21 19:08:36 +08:00
										 |  |  |     // ignore: unused_element
 | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     this.enabled = true, | 
					
						
							|  |  |  |   }) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     var index = keys.indexOf(initialKey); | 
					
						
							|  |  |  |     if (index < 0) { | 
					
						
							|  |  |  |       index = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     var ref = values[index].obs; | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |     current = keys[index]; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     return Container( | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |       decoration: BoxDecoration(border: Border.all(color: MyTheme.border)), | 
					
						
							|  |  |  |       height: 30, | 
					
						
							|  |  |  |       child: Obx(() => DropdownButton<String>( | 
					
						
							|  |  |  |             isExpanded: true, | 
					
						
							|  |  |  |             value: ref.value, | 
					
						
							|  |  |  |             elevation: 16, | 
					
						
							|  |  |  |             underline: Container( | 
					
						
							|  |  |  |               height: 25, | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |             icon: const Icon( | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |               Icons.expand_more_sharp, | 
					
						
							|  |  |  |               size: 20, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             onChanged: enabled | 
					
						
							|  |  |  |                 ? (String? newValue) { | 
					
						
							|  |  |  |                     if (newValue != null && newValue != ref.value) { | 
					
						
							|  |  |  |                       ref.value = newValue; | 
					
						
							|  |  |  |                       current = newValue; | 
					
						
							|  |  |  |                       onChanged(keys[values.indexOf(newValue)]); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 : null, | 
					
						
							|  |  |  |             items: values.map<DropdownMenuItem<String>>((String value) { | 
					
						
							|  |  |  |               return DropdownMenuItem<String>( | 
					
						
							|  |  |  |                 value: value, | 
					
						
							|  |  |  |                 child: Text( | 
					
						
							|  |  |  |                   value, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:43 +08:00
										 |  |  |                   style: const TextStyle(fontSize: _kContentFontSize), | 
					
						
							| 
									
										
										
										
											2022-08-15 11:08:42 +08:00
										 |  |  |                   overflow: TextOverflow.ellipsis, | 
					
						
							|  |  |  |                 ).marginOnly(left: 5), | 
					
						
							|  |  |  |               ); | 
					
						
							|  |  |  |             }).toList(), | 
					
						
							|  |  |  |           )), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#endregion
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#region dialogs
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void changeSocks5Proxy() async { | 
					
						
							|  |  |  |   var socks = await bind.mainGetSocks(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |   String proxy = ''; | 
					
						
							|  |  |  |   String proxyMsg = ''; | 
					
						
							|  |  |  |   String username = ''; | 
					
						
							|  |  |  |   String password = ''; | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |   if (socks.length == 3) { | 
					
						
							|  |  |  |     proxy = socks[0]; | 
					
						
							|  |  |  |     username = socks[1]; | 
					
						
							|  |  |  |     password = socks[2]; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-28 21:43:18 +08:00
										 |  |  |   var proxyController = TextEditingController(text: proxy); | 
					
						
							|  |  |  |   var userController = TextEditingController(text: username); | 
					
						
							|  |  |  |   var pwdController = TextEditingController(text: password); | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   var isInProgress = false; | 
					
						
							|  |  |  |   gFFI.dialogManager.show((setState, close) { | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |     submit() async { | 
					
						
							|  |  |  |       setState(() { | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |         proxyMsg = ''; | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |         isInProgress = true; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       cancel() { | 
					
						
							|  |  |  |         setState(() { | 
					
						
							|  |  |  |           isInProgress = false; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       proxy = proxyController.text.trim(); | 
					
						
							|  |  |  |       username = userController.text.trim(); | 
					
						
							|  |  |  |       password = pwdController.text.trim(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (proxy.isNotEmpty) { | 
					
						
							|  |  |  |         proxyMsg = translate(await bind.mainTestIfValidServer(server: proxy)); | 
					
						
							|  |  |  |         if (proxyMsg.isEmpty) { | 
					
						
							|  |  |  |           // ignore
 | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           cancel(); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       await bind.mainSetSocks( | 
					
						
							|  |  |  |           proxy: proxy, username: username, password: password); | 
					
						
							|  |  |  |       close(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     return CustomAlertDialog( | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |       title: Text(translate('Socks5 Proxy')), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |       content: ConstrainedBox( | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |         constraints: const BoxConstraints(minWidth: 500), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |         child: Column( | 
					
						
							|  |  |  |           crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |           children: [ | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |             const SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |               height: 8.0, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 ConstrainedBox( | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                     constraints: const BoxConstraints(minWidth: 100), | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                     child: Text('${translate("Hostname")}:') | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                         .marginOnly(bottom: 16.0)), | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                 const SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                   width: 24.0, | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: TextField( | 
					
						
							|  |  |  |                     decoration: InputDecoration( | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                         border: const OutlineInputBorder(), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                         errorText: proxyMsg.isNotEmpty ? proxyMsg : null), | 
					
						
							| 
									
										
										
										
											2022-08-28 21:43:18 +08:00
										 |  |  |                     controller: proxyController, | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                     focusNode: FocusNode()..requestFocus(), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |             const SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |               height: 8.0, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 ConstrainedBox( | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                     constraints: const BoxConstraints(minWidth: 100), | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                     child: Text('${translate("Username")}:') | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                         .marginOnly(bottom: 16.0)), | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                 const SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                   width: 24.0, | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: TextField( | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                     decoration: const InputDecoration( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                       border: OutlineInputBorder(), | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2022-08-28 21:43:18 +08:00
										 |  |  |                     controller: userController, | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |             const SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |               height: 8.0, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             Row( | 
					
						
							|  |  |  |               children: [ | 
					
						
							|  |  |  |                 ConstrainedBox( | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                     constraints: const BoxConstraints(minWidth: 100), | 
					
						
							| 
									
										
										
										
											2022-11-10 21:25:12 +08:00
										 |  |  |                     child: Text('${translate("Password")}:') | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                         .marginOnly(bottom: 16.0)), | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                 const SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                   width: 24.0, | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 Expanded( | 
					
						
							|  |  |  |                   child: TextField( | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |                     decoration: const InputDecoration( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                       border: OutlineInputBorder(), | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2022-08-28 21:43:18 +08:00
										 |  |  |                     controller: pwdController, | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ], | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |             const SizedBox( | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |               height: 8.0, | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |             Offstage( | 
					
						
							|  |  |  |                 offstage: !isInProgress, child: const LinearProgressIndicator()) | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       actions: [ | 
					
						
							| 
									
										
										
										
											2023-01-15 19:46:16 +08:00
										 |  |  |         dialogButton('Cancel', onPressed: close, isOutline: true), | 
					
						
							|  |  |  |         dialogButton('OK', onPressed: submit), | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |       ], | 
					
						
							| 
									
										
										
										
											2022-09-03 18:19:50 +08:00
										 |  |  |       onSubmit: submit, | 
					
						
							|  |  |  |       onCancel: close, | 
					
						
							| 
									
										
										
										
											2022-08-13 12:43:35 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#endregion
 |