| 
									
										
										
										
											2023-03-07 19:10:37 +08:00
										 |  |  | import 'dart:io'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  | import 'package:file_picker/file_picker.dart'; | 
					
						
							|  |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/common.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-01 14:18:46 +08:00
										 |  |  | import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  | import 'package:flutter_hbb/models/platform_model.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-01 14:18:46 +08:00
										 |  |  | import 'package:flutter_hbb/models/state_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							|  |  |  | import 'package:url_launcher/url_launcher_string.dart'; | 
					
						
							|  |  |  | import 'package:window_manager/window_manager.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class InstallPage extends StatefulWidget { | 
					
						
							|  |  |  |   const InstallPage({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<InstallPage> createState() => _InstallPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-01 14:18:46 +08:00
										 |  |  | class _InstallPageState extends State<InstallPage> { | 
					
						
							|  |  |  |   final tabController = DesktopTabController(tabType: DesktopTabType.main); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |     Get.put<DesktopTabController>(tabController); | 
					
						
							| 
									
										
										
										
											2023-05-12 16:29:10 +08:00
										 |  |  |     const label = "install"; | 
					
						
							| 
									
										
										
										
											2023-03-01 14:18:46 +08:00
										 |  |  |     tabController.add(TabInfo( | 
					
						
							| 
									
										
										
										
											2023-05-12 16:29:10 +08:00
										 |  |  |         key: label, | 
					
						
							|  |  |  |         label: label, | 
					
						
							| 
									
										
										
										
											2023-03-01 14:18:46 +08:00
										 |  |  |         closable: false, | 
					
						
							|  |  |  |         page: _InstallPageBody( | 
					
						
							| 
									
										
										
										
											2023-05-12 16:29:10 +08:00
										 |  |  |           key: const ValueKey(label), | 
					
						
							| 
									
										
										
										
											2023-03-01 14:18:46 +08:00
										 |  |  |         ))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |     Get.delete<DesktopTabController>(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return DragToResizeArea( | 
					
						
							|  |  |  |       resizeEdgeSize: stateGlobal.resizeEdgeSize.value, | 
					
						
							|  |  |  |       child: Container( | 
					
						
							|  |  |  |         child: Scaffold( | 
					
						
							|  |  |  |             backgroundColor: Theme.of(context).colorScheme.background, | 
					
						
							|  |  |  |             body: DesktopTab(controller: tabController)), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _InstallPageBody extends StatefulWidget { | 
					
						
							|  |  |  |   const _InstallPageBody({Key? key}) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<_InstallPageBody> createState() => _InstallPageBodyState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _InstallPageBodyState extends State<_InstallPageBody> | 
					
						
							|  |  |  |     with WindowListener { | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |   late final TextEditingController controller; | 
					
						
							|  |  |  |   final RxBool startmenu = true.obs; | 
					
						
							|  |  |  |   final RxBool desktopicon = true.obs; | 
					
						
							| 
									
										
										
										
											2023-03-08 11:14:29 +08:00
										 |  |  |   final RxBool driverCert = true.obs; | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |   final RxBool showProgress = false.obs; | 
					
						
							|  |  |  |   final RxBool btnEnabled = true.obs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     windowManager.addListener(this); | 
					
						
							|  |  |  |     controller = TextEditingController(text: bind.installInstallPath()); | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     windowManager.removeListener(this); | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void onWindowClose() { | 
					
						
							|  |  |  |     gFFI.close(); | 
					
						
							|  |  |  |     super.onWindowClose(); | 
					
						
							|  |  |  |     windowManager.setPreventClose(false); | 
					
						
							|  |  |  |     windowManager.close(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     final double em = 13; | 
					
						
							|  |  |  |     final btnFontSize = 0.9 * em; | 
					
						
							|  |  |  |     final double button_radius = 6; | 
					
						
							| 
									
										
										
										
											2023-02-27 17:54:18 +08:00
										 |  |  |     final isDarkTheme = MyTheme.currentThemeMode() == ThemeMode.dark; | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |     final buttonStyle = OutlinedButton.styleFrom( | 
					
						
							|  |  |  |         shape: RoundedRectangleBorder( | 
					
						
							|  |  |  |       borderRadius: BorderRadius.all(Radius.circular(button_radius)), | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |     final inputBorder = OutlineInputBorder( | 
					
						
							|  |  |  |         borderRadius: BorderRadius.zero, | 
					
						
							| 
									
										
										
										
											2023-02-27 17:54:18 +08:00
										 |  |  |         borderSide: | 
					
						
							|  |  |  |             BorderSide(color: isDarkTheme ? Colors.white70 : Colors.black12)); | 
					
						
							|  |  |  |     final textColor = isDarkTheme ? null : Colors.black87; | 
					
						
							|  |  |  |     final dividerColor = isDarkTheme ? Colors.white70 : Colors.black87; | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |     return Scaffold( | 
					
						
							| 
									
										
										
										
											2023-02-27 17:54:18 +08:00
										 |  |  |         backgroundColor: null, | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |         body: SingleChildScrollView( | 
					
						
							|  |  |  |           child: Column( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               Row( | 
					
						
							|  |  |  |                 children: [ | 
					
						
							|  |  |  |                   Text( | 
					
						
							|  |  |  |                     translate('Installation'), | 
					
						
							|  |  |  |                     style: TextStyle( | 
					
						
							|  |  |  |                         fontSize: 2 * em, fontWeight: FontWeight.w500), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |               Row( | 
					
						
							|  |  |  |                 children: [ | 
					
						
							|  |  |  |                   Text('${translate('Installation Path')}: '), | 
					
						
							|  |  |  |                   Expanded( | 
					
						
							|  |  |  |                       child: TextField( | 
					
						
							|  |  |  |                     controller: controller, | 
					
						
							|  |  |  |                     readOnly: true, | 
					
						
							|  |  |  |                     style: TextStyle( | 
					
						
							|  |  |  |                         fontSize: 1.5 * em, fontWeight: FontWeight.w400), | 
					
						
							|  |  |  |                     decoration: InputDecoration( | 
					
						
							|  |  |  |                       isDense: true, | 
					
						
							|  |  |  |                       contentPadding: EdgeInsets.all(0.75 * em), | 
					
						
							|  |  |  |                       enabledBorder: inputBorder, | 
					
						
							|  |  |  |                       border: inputBorder, | 
					
						
							|  |  |  |                       focusedBorder: inputBorder, | 
					
						
							|  |  |  |                       constraints: BoxConstraints(maxHeight: 3 * em), | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                   )), | 
					
						
							|  |  |  |                   Obx(() => OutlinedButton( | 
					
						
							|  |  |  |                           onPressed: | 
					
						
							|  |  |  |                               btnEnabled.value ? selectInstallPath : null, | 
					
						
							|  |  |  |                           style: buttonStyle, | 
					
						
							|  |  |  |                           child: Text(translate('Change Path'), | 
					
						
							|  |  |  |                               style: TextStyle( | 
					
						
							| 
									
										
										
										
											2023-02-27 17:54:18 +08:00
										 |  |  |                                   color: textColor, fontSize: btnFontSize))) | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |                       .marginOnly(left: em)) | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |               ).marginSymmetric(vertical: 2 * em), | 
					
						
							| 
									
										
										
										
											2023-03-08 15:57:33 +08:00
										 |  |  |               TextButton( | 
					
						
							|  |  |  |                 onPressed: () => startmenu.value = !startmenu.value, | 
					
						
							|  |  |  |                 child: Row( | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     Obx(() => Checkbox( | 
					
						
							|  |  |  |                         value: startmenu.value, | 
					
						
							|  |  |  |                         onChanged: (b) { | 
					
						
							|  |  |  |                           if (b != null) startmenu.value = b; | 
					
						
							|  |  |  |                         })), | 
					
						
							|  |  |  |                     RichText( | 
					
						
							|  |  |  |                       text: TextSpan( | 
					
						
							|  |  |  |                         text: translate('Create start menu shortcuts'), | 
					
						
							|  |  |  |                         style: DefaultTextStyle.of(context).style, | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                   ], | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-03-08 15:57:33 +08:00
										 |  |  |               TextButton( | 
					
						
							|  |  |  |                 onPressed: () => desktopicon.value = !desktopicon.value, | 
					
						
							| 
									
										
										
										
											2023-03-07 19:10:37 +08:00
										 |  |  |                 child: Row( | 
					
						
							|  |  |  |                   children: [ | 
					
						
							|  |  |  |                     Obx(() => Checkbox( | 
					
						
							| 
									
										
										
										
											2023-03-08 15:57:33 +08:00
										 |  |  |                         value: desktopicon.value, | 
					
						
							| 
									
										
										
										
											2023-03-07 19:10:37 +08:00
										 |  |  |                         onChanged: (b) { | 
					
						
							| 
									
										
										
										
											2023-03-08 15:57:33 +08:00
										 |  |  |                           if (b != null) desktopicon.value = b; | 
					
						
							| 
									
										
										
										
											2023-03-07 19:10:37 +08:00
										 |  |  |                         })), | 
					
						
							| 
									
										
										
										
											2023-03-08 15:57:33 +08:00
										 |  |  |                     RichText( | 
					
						
							|  |  |  |                       text: TextSpan( | 
					
						
							|  |  |  |                         text: translate('Create desktop icon'), | 
					
						
							|  |  |  |                         style: DefaultTextStyle.of(context).style, | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2023-03-07 19:10:37 +08:00
										 |  |  |                   ], | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-03-08 15:57:33 +08:00
										 |  |  |               Offstage( | 
					
						
							|  |  |  |                 offstage: !Platform.isWindows, | 
					
						
							|  |  |  |                 child: TextButton( | 
					
						
							|  |  |  |                   onPressed: () => driverCert.value = !driverCert.value, | 
					
						
							|  |  |  |                   child: Row( | 
					
						
							|  |  |  |                     children: [ | 
					
						
							|  |  |  |                       Obx(() => Checkbox( | 
					
						
							|  |  |  |                           value: driverCert.value, | 
					
						
							|  |  |  |                           onChanged: (b) { | 
					
						
							|  |  |  |                             if (b != null) driverCert.value = b; | 
					
						
							|  |  |  |                           })), | 
					
						
							|  |  |  |                       RichText( | 
					
						
							|  |  |  |                         text: TextSpan( | 
					
						
							|  |  |  |                           text: translate('idd_driver_tip'), | 
					
						
							|  |  |  |                           style: DefaultTextStyle.of(context).style, | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |               GestureDetector( | 
					
						
							|  |  |  |                   onTap: () => launchUrlString('http://rustdesk.com/privacy'), | 
					
						
							|  |  |  |                   child: Row( | 
					
						
							|  |  |  |                     children: [ | 
					
						
							|  |  |  |                       Text(translate('End-user license agreement'), | 
					
						
							|  |  |  |                           style: const TextStyle( | 
					
						
							|  |  |  |                               decoration: TextDecoration.underline)) | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                   )).marginOnly(top: 2 * em), | 
					
						
							|  |  |  |               Row(children: [Text(translate('agreement_tip'))]) | 
					
						
							|  |  |  |                   .marginOnly(top: em), | 
					
						
							| 
									
										
										
										
											2023-02-27 17:54:18 +08:00
										 |  |  |               Divider(color: dividerColor).marginSymmetric(vertical: 0.5 * em), | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |               Row( | 
					
						
							|  |  |  |                 children: [ | 
					
						
							|  |  |  |                   Expanded( | 
					
						
							|  |  |  |                       child: Obx(() => Offstage( | 
					
						
							|  |  |  |                             offstage: !showProgress.value, | 
					
						
							|  |  |  |                             child: LinearProgressIndicator(), | 
					
						
							|  |  |  |                           ))), | 
					
						
							|  |  |  |                   Obx(() => OutlinedButton( | 
					
						
							|  |  |  |                           onPressed: btnEnabled.value | 
					
						
							|  |  |  |                               ? () => windowManager.close() | 
					
						
							|  |  |  |                               : null, | 
					
						
							|  |  |  |                           style: buttonStyle, | 
					
						
							|  |  |  |                           child: Text(translate('Cancel'), | 
					
						
							|  |  |  |                               style: TextStyle( | 
					
						
							| 
									
										
										
										
											2023-02-27 17:54:18 +08:00
										 |  |  |                                   color: textColor, fontSize: btnFontSize))) | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |                       .marginOnly(right: 2 * em)), | 
					
						
							|  |  |  |                   Obx(() => ElevatedButton( | 
					
						
							|  |  |  |                       onPressed: btnEnabled.value ? install : null, | 
					
						
							|  |  |  |                       style: ElevatedButton.styleFrom( | 
					
						
							|  |  |  |                           primary: MyTheme.button, | 
					
						
							|  |  |  |                           shape: RoundedRectangleBorder( | 
					
						
							|  |  |  |                             borderRadius: BorderRadius.all( | 
					
						
							|  |  |  |                                 Radius.circular(button_radius)), | 
					
						
							|  |  |  |                           )), | 
					
						
							|  |  |  |                       child: Text( | 
					
						
							|  |  |  |                         translate('Accept and Install'), | 
					
						
							|  |  |  |                         style: TextStyle(fontSize: btnFontSize), | 
					
						
							|  |  |  |                       ))), | 
					
						
							|  |  |  |                   Offstage( | 
					
						
							|  |  |  |                     offstage: bind.installShowRunWithoutInstall(), | 
					
						
							|  |  |  |                     child: Obx(() => OutlinedButton( | 
					
						
							|  |  |  |                             onPressed: btnEnabled.value | 
					
						
							|  |  |  |                                 ? () => bind.installRunWithoutInstall() | 
					
						
							|  |  |  |                                 : null, | 
					
						
							|  |  |  |                             style: buttonStyle, | 
					
						
							|  |  |  |                             child: Text(translate('Run without install'), | 
					
						
							|  |  |  |                                 style: TextStyle( | 
					
						
							| 
									
										
										
										
											2023-02-27 17:54:18 +08:00
										 |  |  |                                     color: textColor, fontSize: btnFontSize))) | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |                         .marginOnly(left: 2 * em)), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           ).paddingSymmetric(horizontal: 8 * em, vertical: 2 * em), | 
					
						
							|  |  |  |         )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void install() { | 
					
						
							| 
									
										
										
										
											2023-03-08 11:14:29 +08:00
										 |  |  |     do_install() { | 
					
						
							|  |  |  |       btnEnabled.value = false; | 
					
						
							|  |  |  |       showProgress.value = true; | 
					
						
							|  |  |  |       String args = ''; | 
					
						
							|  |  |  |       if (startmenu.value) args += ' startmenu'; | 
					
						
							|  |  |  |       if (desktopicon.value) args += ' desktopicon'; | 
					
						
							|  |  |  |       if (driverCert.value) args += ' driverCert'; | 
					
						
							|  |  |  |       bind.installInstallMe(options: args, path: controller.text); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (driverCert.isTrue) { | 
					
						
							|  |  |  |       final tag = 'install-info-install-cert-confirm'; | 
					
						
							|  |  |  |       final btns = [ | 
					
						
							|  |  |  |         dialogButton( | 
					
						
							|  |  |  |           'Cancel', | 
					
						
							|  |  |  |           onPressed: () => gFFI.dialogManager.dismissByTag(tag), | 
					
						
							|  |  |  |           isOutline: true, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         dialogButton( | 
					
						
							|  |  |  |           'OK', | 
					
						
							|  |  |  |           onPressed: () { | 
					
						
							|  |  |  |             gFFI.dialogManager.dismissByTag(tag); | 
					
						
							|  |  |  |             do_install(); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           isOutline: false, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ]; | 
					
						
							|  |  |  |       gFFI.dialogManager.show( | 
					
						
							| 
									
										
										
										
											2023-05-08 12:34:19 +08:00
										 |  |  |         (setState, close, context) => CustomAlertDialog( | 
					
						
							| 
									
										
										
										
											2023-03-08 11:14:29 +08:00
										 |  |  |           title: null, | 
					
						
							|  |  |  |           content: SelectionArea( | 
					
						
							| 
									
										
										
										
											2023-03-08 15:57:33 +08:00
										 |  |  |               child: | 
					
						
							|  |  |  |                   msgboxContent('info', 'Warning', 'confirm_idd_driver_tip')), | 
					
						
							| 
									
										
										
										
											2023-03-08 11:14:29 +08:00
										 |  |  |           actions: btns, | 
					
						
							|  |  |  |           onCancel: close, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         tag: tag, | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       do_install(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-10-09 20:32:28 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void selectInstallPath() async { | 
					
						
							|  |  |  |     String? install_path = await FilePicker.platform | 
					
						
							|  |  |  |         .getDirectoryPath(initialDirectory: controller.text); | 
					
						
							|  |  |  |     if (install_path != null) { | 
					
						
							|  |  |  |       install_path = '$install_path\\${await bind.mainGetAppName()}'; | 
					
						
							|  |  |  |       controller.text = install_path; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |