From 089cf41a2ff86b71696e2591ac74daf0cfef17eb Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 9 Oct 2022 20:32:28 +0800 Subject: [PATCH] add install page Signed-off-by: 21pages --- flutter/lib/consts.dart | 2 +- flutter/lib/desktop/pages/install_page.dart | 198 ++++++++++++++++++++ flutter/lib/main.dart | 27 +++ src/flutter_ffi.rs | 16 ++ 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 flutter/lib/desktop/pages/install_page.dart diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index d31f77111..ce6c93c00 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -3,7 +3,7 @@ import 'dart:io'; const double kDesktopRemoteTabBarHeight = 28.0; -/// [kAppTypeMain] used by 'Desktop Main Page' , 'Mobile (Client and Server)' , 'Desktop CM Page' +/// [kAppTypeMain] used by 'Desktop Main Page' , 'Mobile (Client and Server)' , 'Desktop CM Page', "Install Page" const String kAppTypeMain = "main"; const String kAppTypeDesktopRemote = "remote"; const String kAppTypeDesktopFileTransfer = "file transfer"; diff --git a/flutter/lib/desktop/pages/install_page.dart b/flutter/lib/desktop/pages/install_page.dart new file mode 100644 index 000000000..b7b3f982d --- /dev/null +++ b/flutter/lib/desktop/pages/install_page.dart @@ -0,0 +1,198 @@ +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/models/platform_model.dart'; +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 createState() => _InstallPageState(); +} + +class _InstallPageState extends State with WindowListener { + late final TextEditingController controller; + final RxBool startmenu = true.obs; + final RxBool desktopicon = true.obs; + 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; + final buttonStyle = OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(button_radius)), + )); + final inputBorder = OutlineInputBorder( + borderRadius: BorderRadius.zero, + borderSide: BorderSide(color: Colors.black12)); + return Scaffold( + backgroundColor: Colors.white, + 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( + color: Colors.black87, + fontSize: btnFontSize))) + .marginOnly(left: em)) + ], + ).marginSymmetric(vertical: 2 * em), + Row( + children: [ + Obx(() => Checkbox( + value: startmenu.value, + onChanged: (b) { + if (b != null) startmenu.value = b; + })), + Text(translate('Create start menu shortcuts')) + ], + ), + Row( + children: [ + Obx(() => Checkbox( + value: desktopicon.value, + onChanged: (b) { + if (b != null) desktopicon.value = b; + })), + Text(translate('Create desktop icon')) + ], + ), + 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), + Divider(color: Colors.black87) + .marginSymmetric(vertical: 0.5 * em), + 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( + color: Colors.black87, + fontSize: btnFontSize))) + .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( + color: Colors.black87, + fontSize: btnFontSize))) + .marginOnly(left: 2 * em)), + ), + ], + ) + ], + ).paddingSymmetric(horizontal: 8 * em, vertical: 2 * em), + )); + } + + void install() { + btnEnabled.value = false; + showProgress.value = true; + String args = ''; + if (startmenu.value) args += 'startmenu '; + if (desktopicon.value) args += 'desktopicon '; + bind.installInstallMe(options: args, path: controller.text); + } + + 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; + } + } +} diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 0d1123e05..27b14be09 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -4,6 +4,7 @@ import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart'; import 'package:flutter_hbb/desktop/pages/server_page.dart'; +import 'package:flutter_hbb/desktop/pages/install_page.dart'; import 'package:flutter_hbb/desktop/screen/desktop_file_transfer_screen.dart'; import 'package:flutter_hbb/desktop/screen/desktop_port_forward_screen.dart'; import 'package:flutter_hbb/desktop/screen/desktop_remote_screen.dart'; @@ -64,6 +65,8 @@ Future main(List args) async { desktopType = DesktopType.cm; await windowManager.ensureInitialized(); runConnectionManagerScreen(); + } else if (args.contains('--install')) { + runInstallPage(); } else { desktopType = DesktopType.main; await windowManager.ensureInitialized(); @@ -215,6 +218,30 @@ void runConnectionManagerScreen() async { }); } +void runInstallPage() async { + await windowManager.ensureInitialized(); + await initEnv(kAppTypeMain); + runApp(GetMaterialApp( + debugShowCheckedModeBanner: false, + theme: MyTheme.lightTheme, + themeMode: ThemeMode.light, + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: supportedLocales, + home: const InstallPage(), + builder: _keepScaleBuilder())); + windowManager.waitUntilReadyToShow( + WindowOptions(size: Size(800, 600), center: true), () async { + windowManager.show(); + windowManager.focus(); + windowManager.setOpacity(1); + windowManager.setAlignment(Alignment.center); // ensure + }); +} + WindowOptions getHiddenTitleBarWindowOptions({Size? size}) { return WindowOptions( size: size, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 9ce92aeb2..b33c9490f 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1041,6 +1041,22 @@ pub fn main_update_me() -> SyncReturn { SyncReturn(true) } +pub fn install_show_run_without_install() -> SyncReturn { + SyncReturn(show_run_without_install()) +} + +pub fn install_run_without_install() { + run_without_install(); +} + +pub fn install_install_me(options: String, path: String) { + install_me(options, path, false, false); +} + +pub fn install_install_path() -> SyncReturn { + SyncReturn(install_path()) +} + #[cfg(target_os = "android")] pub mod server_side { use jni::{