Merge pull request #829 from Kingtous/flutter_desktop
feat: file transfer - initial migration to rust bridge
This commit is contained in:
		
						commit
						c1cf9307ac
					
				| @ -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'; | ||||
| @ -36,14 +35,13 @@ class _FileManagerPageState extends State<FileManagerPage> | ||||
|   @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 | ||||
| @ -80,24 +78,24 @@ class _FileManagerPageState extends State<FileManagerPage> | ||||
|                     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<String>( | ||||
|                         icon: Icon(Icons.more_vert), | ||||
| @ -197,7 +195,12 @@ class _FileManagerPageState extends State<FileManagerPage> | ||||
|                         }), | ||||
|                   ], | ||||
|                 ), | ||||
|                 body: body(), | ||||
|                 body: Row( | ||||
|                   children: [ | ||||
|                     Flexible(flex: 1, child: body(isLocal: true)), | ||||
|                     Flexible(flex: 1, child: body(isLocal: false)) | ||||
|                   ], | ||||
|                 ), | ||||
|                 bottomSheet: bottomSheet(), | ||||
|               )); | ||||
|         })); | ||||
| @ -210,9 +213,8 @@ class _FileManagerPageState extends State<FileManagerPage> | ||||
|     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(), | ||||
|  | ||||
| @ -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 { | ||||
| @ -519,6 +524,10 @@ class FileModel extends ChangeNotifier { | ||||
|     _currentRemoteDir.changeSortStyle(sort); | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   initFileFetcher() { | ||||
|     _fileFetcher.id = _ffi.target?.id; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class JobResultListener<T> { | ||||
| @ -566,6 +575,17 @@ class FileFetcher { | ||||
|   Map<String, Completer<FileDirectory>> remoteTasks = Map(); | ||||
|   Map<int, Completer<FileDirectory>> 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<FileDirectory> 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 +653,14 @@ class FileFetcher { | ||||
|   Future<FileDirectory> 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 +678,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); | ||||
|  | ||||
| @ -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<String, dynamic> 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<String, dynamic> 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. | ||||
|  | ||||
| @ -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); | ||||
|       } | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -24,10 +24,9 @@ lazy_static::lazy_static! { | ||||
|     pub static ref SOFTWARE_UPDATE_URL: Arc<Mutex<String>> = Default::default(); | ||||
| } | ||||
| 
 | ||||
| #[cfg(any(target_os = "android", target_os = "ios"))] | ||||
| lazy_static::lazy_static! { | ||||
|     pub static ref MOBILE_INFO1: Arc<Mutex<String>> = Default::default(); | ||||
|     pub static ref MOBILE_INFO2: Arc<Mutex<String>> = Default::default(); | ||||
|     pub static ref FLUTTER_INFO1: Arc<Mutex<String>> = Default::default(); | ||||
|     pub static ref FLUTTER_INFO2: Arc<Mutex<String>> = Default::default(); | ||||
| } | ||||
| 
 | ||||
| #[inline] | ||||
|  | ||||
| @ -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::<HashMap<String, String>>(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::<HashMap<String, String>>(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);
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user