From 0eacb6706a28cac89ef3d660a2adc26e8f046bdc Mon Sep 17 00:00:00 2001 From: Kingtous Date: Tue, 21 Jun 2022 17:58:27 +0800 Subject: [PATCH 1/3] feat: file transfer tab works Signed-off-by: Kingtous --- .../lib/desktop/pages/file_manager_page.dart | 9 ++-- flutter/lib/models/file_model.dart | 25 ++++++++-- flutter/lib/models/model.dart | 49 +++++++++++-------- flutter/lib/models/native_model.dart | 7 ++- flutter/macos/Runner/bridge_generated.h | 6 +++ src/common.rs | 5 +- src/flutter_ffi.rs | 45 ++++++++++------- 7 files changed, 93 insertions(+), 53 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 162e9d720..0deb6741d 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -36,14 +36,13 @@ class _FileManagerPageState extends State @override void initState() { super.initState(); - Get.put(FFI(), tag: 'ft_${widget.id}'); - _ffi.ffiModel.platformFFI = gFFI.ffiModel.platformFFI; - - _ffi.connect(widget.id, isFileTransfer: true); - _ffi.ffiModel.updateEventListener(widget.id); + Get.put(FFI.newFFI()..connect(widget.id, isFileTransfer: true), + tag: 'ft_${widget.id}'); + // _ffi.ffiModel.updateEventListener(widget.id); if (!Platform.isLinux) { Wakelock.enable(); } + print("init success with id ${widget.id}"); } @override diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 0f7ce0df2..aefdcf639 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -519,6 +519,10 @@ class FileModel extends ChangeNotifier { _currentRemoteDir.changeSortStyle(sort); notifyListeners(); } + + initFileFetcher() { + _fileFetcher.id = _ffi.target?.id; + } } class JobResultListener { @@ -566,6 +570,17 @@ class FileFetcher { Map> remoteTasks = Map(); Map> readRecursiveTasks = Map(); + String? _id; + + String? get id => _id; + + set id(String? id) { + _id = id; + } + + // if id == null, means to fetch global FFI + FFI get _ffi => ffi(_id == null ? "" : 'ft_${_id}'); + Future registerReadTask(bool isLocal, String path) { // final jobs = isLocal?localJobs:remoteJobs; // maybe we will use read local dir async later final tasks = remoteTasks; // bypass now @@ -633,13 +648,14 @@ class FileFetcher { Future fetchDirectory( String path, bool isLocal, bool showHidden) async { try { - final msg = {"path": path, "show_hidden": showHidden.toString()}; if (isLocal) { - final res = gFFI.getByName("read_local_dir_sync", jsonEncode(msg)); + final res = await _ffi.bind.sessionReadLocalDirSync( + id: id ?? "", path: path, showHidden: showHidden); final fd = FileDirectory.fromJson(jsonDecode(res)); return fd; } else { - gFFI.setByName("read_remote_dir", jsonEncode(msg)); + await _ffi.bind.sessionReadRemoteDir( + id: id ?? "", path: path, includeHidden: showHidden); return registerReadTask(isLocal, path); } } catch (e) { @@ -657,7 +673,8 @@ class FileFetcher { "show_hidden": showHidden.toString(), "is_remote": (!isLocal).toString() }; - gFFI.setByName("read_dir_recursive", jsonEncode(msg)); + // TODO + _ffi.setByName("read_dir_recursive", jsonEncode(msg)); return registerReadRecursiveTask(id); } catch (e) { return Future.error(e); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 5c383f774..0332dc797 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -784,6 +784,13 @@ class FFI { this.fileModel = FileModel(WeakReference(this)); } + static FFI newFFI() { + final ffi = FFI(); + // keep platformFFI only once + ffi.ffiModel.platformFFI = gFFI.ffiModel.platformFFI; + return ffi; + } + /// Get the remote id for current client. String getId() { return getByName('remote_id'); // TODO @@ -888,32 +895,32 @@ class FFI { setByName('connect_file_transfer', id); } else { chatModel.resetClientMode(); - // setByName('connect', id); - // TODO multi model instances canvasModel.id = id; imageModel._id = id; cursorModel.id = id; - final stream = - bind.sessionConnect(id: id, isFileTransfer: isFileTransfer); - final cb = ffiModel.startEventListener(id); - () async { - await for (final message in stream) { - if (message is Event) { - try { - debugPrint("event:${message.field0}"); - Map event = json.decode(message.field0); - cb(event); - } catch (e) { - print('json.decode fail(): $e'); - } - } else if (message is Rgba) { - imageModel.onRgba(message.field0); - } - } - }(); - // every instance will bind a stream } + final stream = bind.sessionConnect(id: id, isFileTransfer: isFileTransfer); + final cb = ffiModel.startEventListener(id); + () async { + await for (final message in stream) { + if (message is Event) { + try { + debugPrint("event:${message.field0}"); + Map event = json.decode(message.field0); + cb(event); + } catch (e) { + print('json.decode fail(): $e'); + } + } else if (message is Rgba) { + imageModel.onRgba(message.field0); + } + } + }(); + // every instance will bind a stream this.id = id; + if (isFileTransfer) { + this.fileModel.initFileFetcher(); + } } /// Login with [password], choose if the client should [remember] it. diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 1ae523b8b..c0fd4dfa1 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -93,7 +93,12 @@ class PlatformFFI { _ffiBind = RustdeskImpl(dylib); _startListenEvent(_ffiBind); // global event try { - _homeDir = (await ExternalPath.getExternalStorageDirectories())[0]; + if (isAndroid) { + // only support for android + _homeDir = (await ExternalPath.getExternalStorageDirectories())[0]; + } else { + _homeDir = (await getDownloadsDirectory())?.path ?? ""; + } } catch (e) { print(e); } diff --git a/flutter/macos/Runner/bridge_generated.h b/flutter/macos/Runner/bridge_generated.h index 20f318836..6eb6cbd51 100644 --- a/flutter/macos/Runner/bridge_generated.h +++ b/flutter/macos/Runner/bridge_generated.h @@ -149,6 +149,11 @@ void wire_session_create_dir(int64_t port_, struct wire_uint_8_list *path, bool is_remote); +void wire_session_read_local_dir_sync(int64_t port_, + struct wire_uint_8_list *id, + struct wire_uint_8_list *path, + bool show_hidden); + struct wire_uint_8_list *new_uint_8_list(int32_t len); void free_WireSyncReturnStruct(struct WireSyncReturnStruct val); @@ -194,6 +199,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) wire_session_remove_all_empty_dirs); dummy_var ^= ((int64_t) (void*) wire_session_cancel_job); dummy_var ^= ((int64_t) (void*) wire_session_create_dir); + dummy_var ^= ((int64_t) (void*) wire_session_read_local_dir_sync); dummy_var ^= ((int64_t) (void*) new_uint_8_list); dummy_var ^= ((int64_t) (void*) free_WireSyncReturnStruct); dummy_var ^= ((int64_t) (void*) store_dart_post_cobject); diff --git a/src/common.rs b/src/common.rs index 03e5f4f4b..b3141a7fe 100644 --- a/src/common.rs +++ b/src/common.rs @@ -24,10 +24,9 @@ lazy_static::lazy_static! { pub static ref SOFTWARE_UPDATE_URL: Arc> = Default::default(); } -#[cfg(any(target_os = "android", target_os = "ios"))] lazy_static::lazy_static! { - pub static ref MOBILE_INFO1: Arc> = Default::default(); - pub static ref MOBILE_INFO2: Arc> = Default::default(); + pub static ref FLUTTER_INFO1: Arc> = Default::default(); + pub static ref FLUTTER_INFO2: Arc> = Default::default(); } #[inline] diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 25c37d418..055e62721 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -307,6 +307,15 @@ pub fn session_create_dir(id: String, act_id: i32, path: String, is_remote: bool } } +pub fn session_read_local_dir_sync(id: String, path: String, show_hidden: bool) -> String { + if let Some(session) = SESSIONS.read().unwrap().get(&id) { + if let Ok(fd) = fs::read_dir(&fs::get_path(&path), show_hidden) { + return make_fd_to_json(fd); + } + } + "".to_string() +} + /// FFI for **get** commands which are idempotent. /// Return result in c string. /// @@ -397,21 +406,21 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co "get_home_dir" => { res = fs::get_home_as_string(); } - "read_local_dir_sync" => { - if let Ok(value) = arg.to_str() { - if let Ok(m) = serde_json::from_str::>(value) { - if let (Some(path), Some(show_hidden)) = - (m.get("path"), m.get("show_hidden")) - { - if let Ok(fd) = - fs::read_dir(&fs::get_path(path), show_hidden.eq("true")) - { - res = make_fd_to_json(fd); - } - } - } - } - } + // "read_local_dir_sync" => { + // if let Ok(value) = arg.to_str() { + // if let Ok(m) = serde_json::from_str::>(value) { + // if let (Some(path), Some(show_hidden)) = + // (m.get("path"), m.get("show_hidden")) + // { + // if let Ok(fd) = + // fs::read_dir(&fs::get_path(path), show_hidden.eq("true")) + // { + // res = make_fd_to_json(fd); + // } + // } + // } + // } + // } // Server Side #[cfg(not(any(target_os = "ios")))] "clients_state" => { @@ -452,13 +461,11 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) { "init" => { initialize(value); } - #[cfg(any(target_os = "android", target_os = "ios"))] "info1" => { - *crate::common::MOBILE_INFO1.lock().unwrap() = value.to_owned(); + *crate::common::FLUTTER_INFO1.lock().unwrap() = value.to_owned(); } - #[cfg(any(target_os = "android", target_os = "ios"))] "info2" => { - *crate::common::MOBILE_INFO2.lock().unwrap() = value.to_owned(); + *crate::common::FLUTTER_INFO2.lock().unwrap() = value.to_owned(); } // "connect" => { // Session::start(value, false); From 02aa676030ef6a3637490225df0810bb54690967 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Tue, 21 Jun 2022 17:58:42 +0800 Subject: [PATCH 2/3] opt: add init frame size Signed-off-by: Kingtous --- flutter/linux/my_application.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/linux/my_application.cc b/flutter/linux/my_application.cc index f726dd76c..8c7a5fe05 100644 --- a/flutter/linux/my_application.cc +++ b/flutter/linux/my_application.cc @@ -50,7 +50,7 @@ static void my_application_activate(GApplication* application) { auto bdw = bitsdojo_window_from(window); // <--- add this line bdw->setCustomFrame(true); // <-- add this line - //gtk_window_set_default_size(window, 1280, 720); // <-- comment this line + gtk_window_set_default_size(window, 1280, 720); // <-- comment this line gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlDartProject) project = fl_dart_project_new(); From 5bfbb1b807d3b98892329096f3971b816a0b8a39 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Tue, 21 Jun 2022 18:14:44 +0800 Subject: [PATCH 3/3] opt: dual columns file-transfer in desktop version Signed-off-by: Kingtous --- .../lib/desktop/pages/file_manager_page.dart | 49 ++++++++++--------- flutter/lib/models/file_model.dart | 7 ++- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 0deb6741d..e9e69556c 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -8,7 +8,6 @@ import 'package:flutter_hbb/models/file_model.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; -import 'package:toggle_switch/toggle_switch.dart'; import 'package:wakelock/wakelock.dart'; import '../../common.dart'; @@ -79,24 +78,24 @@ class _FileManagerPageState extends State IconButton(icon: Icon(Icons.close), onPressed: clientClose), ]), centerTitle: true, - title: ToggleSwitch( - initialLabelIndex: model.isLocal ? 0 : 1, - activeBgColor: [MyTheme.idColor], - inactiveBgColor: MyTheme.grayBg, - inactiveFgColor: Colors.black54, - totalSwitches: 2, - minWidth: 100, - fontSize: 15, - iconSize: 18, - labels: [translate("Local"), translate("Remote")], - icons: [Icons.phone_android_sharp, Icons.screen_share], - onToggle: (index) { - final current = model.isLocal ? 0 : 1; - if (index != current) { - model.togglePage(); - } - }, - ), + // title: ToggleSwitch( + // initialLabelIndex: model.isLocal ? 0 : 1, + // activeBgColor: [MyTheme.idColor], + // inactiveBgColor: MyTheme.grayBg, + // inactiveFgColor: Colors.black54, + // totalSwitches: 2, + // minWidth: 100, + // fontSize: 15, + // iconSize: 18, + // labels: [translate("Local"), translate("Remote")], + // icons: [Icons.phone_android_sharp, Icons.screen_share], + // onToggle: (index) { + // final current = model.isLocal ? 0 : 1; + // if (index != current) { + // model.togglePage(); + // } + // }, + // ), actions: [ PopupMenuButton( icon: Icon(Icons.more_vert), @@ -196,7 +195,12 @@ class _FileManagerPageState extends State }), ], ), - body: body(), + body: Row( + children: [ + Flexible(flex: 1, child: body(isLocal: true)), + Flexible(flex: 1, child: body(isLocal: false)) + ], + ), bottomSheet: bottomSheet(), )); })); @@ -209,9 +213,8 @@ class _FileManagerPageState extends State return !_selectedItems.isOtherPage(model.isLocal); } - Widget body() { - final isLocal = model.isLocal; - final fd = model.currentDir; + Widget body({bool isLocal = false}) { + final fd = isLocal ? model.currentLocalDir : model.currentRemoteDir; final entries = fd.entries; return Column(children: [ headTools(), diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index aefdcf639..9ed8b54d7 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -233,7 +233,12 @@ class FileModel extends ChangeNotifier { } refresh() { - openDirectory(currentDir.path); + if (isDesktop) { + openDirectory(currentRemoteDir.path); + openDirectory(currentLocalDir.path); + } else { + openDirectory(currentDir.path); + } } openDirectory(String path, {bool? isLocal}) async {