From c457b0e7d3a8ca8ad7a0541c180f791890481b4e Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 19 Mar 2026 20:04:10 +0800 Subject: [PATCH] add option to hide stop-service when service is running (#14563) * add option to hide stop-service when service is running Signed-off-by: 21pages * update hbb_common to upstream Signed-off-by: 21pages --------- Signed-off-by: 21pages --- .../flutter_hbb/FloatingWindowService.kt | 6 ++- flutter/android/app/src/main/kotlin/ffi.kt | 1 + flutter/lib/consts.dart | 1 + .../desktop/pages/desktop_setting_page.dart | 33 ++++++++++------ flutter/lib/mobile/pages/server_page.dart | 22 ++++++----- libs/hbb_common | 2 +- src/flutter_ffi.rs | 16 ++++++++ src/tray.rs | 39 +++++++++++++------ src/ui/index.tis | 3 +- 9 files changed, 87 insertions(+), 36 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt index 696d536c6..6dd4a2f61 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt @@ -311,7 +311,10 @@ class FloatingWindowService : Service(), View.OnTouchListener { popupMenu.menu.add(0, idSyncClipboard, 0, translate("Update client clipboard")) } val idStopService = 2 - popupMenu.menu.add(0, idStopService, 0, translate("Stop service")) + val hideStopService = FFI.getBuildinOption("hide-stop-service") == "Y" + if (!hideStopService) { + popupMenu.menu.add(0, idStopService, 0, translate("Stop service")) + } popupMenu.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { idShowRustDesk -> { @@ -389,4 +392,3 @@ class FloatingWindowService : Service(), View.OnTouchListener { return false } } - diff --git a/flutter/android/app/src/main/kotlin/ffi.kt b/flutter/android/app/src/main/kotlin/ffi.kt index 8e9b39968..e3c9d9830 100644 --- a/flutter/android/app/src/main/kotlin/ffi.kt +++ b/flutter/android/app/src/main/kotlin/ffi.kt @@ -24,6 +24,7 @@ object FFI { external fun setFrameRawEnable(name: String, value: Boolean) external fun setCodecInfo(info: String) external fun getLocalOption(key: String): String + external fun getBuildinOption(key: String): String external fun onClipboardUpdate(clips: ByteBuffer) external fun isServiceClipboardEnabled(): Boolean } diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 3b9940c9c..b1112dd29 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -175,6 +175,7 @@ const String kOptionEnableFlutterHttpOnRust = "enable-flutter-http-on-rust"; const String kOptionHideServerSetting = "hide-server-settings"; const String kOptionHideProxySetting = "hide-proxy-settings"; const String kOptionHideWebSocketSetting = "hide-websocket-settings"; +const String kOptionHideStopService = "hide-stop-service"; const String kOptionHideRemotePrinterSetting = "hide-remote-printer-settings"; const String kOptionHideSecuritySetting = "hide-security-settings"; const String kOptionHideNetworkSetting = "hide-network-settings"; diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 82212d191..029629b24 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -458,18 +458,27 @@ class _GeneralState extends State<_General> { return const Offstage(); } - return _Card(title: 'Service', children: [ - Obx(() => _Button(serviceStop.value ? 'Start' : 'Stop', () { - () async { - serviceBtnEnabled.value = false; - await start_service(serviceStop.value); - // enable the button after 1 second - Future.delayed(const Duration(seconds: 1), () { - serviceBtnEnabled.value = true; - }); - }(); - }, enabled: serviceBtnEnabled.value)) - ]); + final hideStopService = + bind.mainGetBuildinOption(key: kOptionHideStopService) == 'Y'; + + return Obx(() { + if (hideStopService && !serviceStop.value) { + return const Offstage(); + } + + return _Card(title: 'Service', children: [ + _Button(serviceStop.value ? 'Start' : 'Stop', () { + () async { + serviceBtnEnabled.value = false; + await start_service(serviceStop.value); + // enable the button after 1 second + Future.delayed(const Duration(seconds: 1), () { + serviceBtnEnabled.value = true; + }); + }(); + }, enabled: serviceBtnEnabled.value) + ]); + }); } Widget other() { diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index 54406ff2e..57856a4d7 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -582,10 +582,13 @@ class _PermissionCheckerState extends State { Widget build(BuildContext context) { final serverModel = Provider.of(context); final hasAudioPermission = androidVersion >= 30; + final hideStopService = + isAndroid && + bind.mainGetBuildinOption(key: kOptionHideStopService) == 'Y'; return PaddingCard( title: translate("Permissions"), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - serverModel.mediaOk + serverModel.mediaOk && !hideStopService ? ElevatedButton.icon( style: ButtonStyle( backgroundColor: @@ -595,14 +598,15 @@ class _PermissionCheckerState extends State { label: Text(translate("Stop service"))) .marginOnly(bottom: 8) : SizedBox.shrink(), - PermissionRow( - translate("Screen Capture"), - serverModel.mediaOk, - !serverModel.mediaOk && - gFFI.userModel.userName.value.isEmpty && - bind.mainGetLocalOption(key: "show-scam-warning") != "N" - ? () => showScamWarning(context, serverModel) - : serverModel.toggleService), + if (!hideStopService || !serverModel.mediaOk) + PermissionRow( + translate("Screen Capture"), + serverModel.mediaOk, + !serverModel.mediaOk && + gFFI.userModel.userName.value.isEmpty && + bind.mainGetLocalOption(key: "show-scam-warning") != "N" + ? () => showScamWarning(context, serverModel) + : serverModel.toggleService), PermissionRow(translate("Input Control"), serverModel.inputOk, serverModel.toggleInput), PermissionRow(translate("Transfer file"), serverModel.fileOk, diff --git a/libs/hbb_common b/libs/hbb_common index 48c37de3e..648b63942 160000 --- a/libs/hbb_common +++ b/libs/hbb_common @@ -1 +1 @@ -Subproject commit 48c37de3e6c4e399af6f51ca20e8e3e1fd037976 +Subproject commit 648b639427953cb8b052b4d80aeb882c644c4ce9 diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 551ad799f..092e6d295 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -3049,6 +3049,22 @@ pub mod server_side { return env.new_string(res).unwrap_or_default().into_raw(); } + #[no_mangle] + pub unsafe extern "system" fn Java_ffi_FFI_getBuildinOption( + env: JNIEnv, + _class: JClass, + key: JString, + ) -> jstring { + let mut env = env; + let res = if let Ok(key) = env.get_string(&key) { + let key: String = key.into(); + super::get_builtin_option(&key) + } else { + "".into() + }; + return env.new_string(res).unwrap_or_default().into_raw(); + } + #[no_mangle] pub unsafe extern "system" fn Java_ffi_FFI_isServiceClipboardEnabled( env: JNIEnv, diff --git a/src/tray.rs b/src/tray.rs index 8ab4e3ecb..e8db0efc0 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -54,9 +54,22 @@ fn make_tray() -> hbb_common::ResultType<()> { let mut event_loop = EventLoopBuilder::new().build(); let tray_menu = Menu::new(); - let quit_i = MenuItem::new(translate("Stop service".to_owned()), true, None); + let hide_stop_service = crate::ui_interface::get_builtin_option( + hbb_common::config::keys::OPTION_HIDE_STOP_SERVICE, + ) == "Y"; + // The tray icon is only shown when the service is running, so we don't need to check + // the `stop-service` option here. + let quit_i = if !hide_stop_service { + Some(MenuItem::new(translate("Stop service".to_owned()), true, None)) + } else { + None + }; let open_i = MenuItem::new(translate("Open".to_owned()), true, None); - tray_menu.append_items(&[&open_i, &quit_i]).ok(); + if let Some(quit_i) = &quit_i { + tray_menu.append_items(&[&open_i, quit_i]).ok(); + } else { + tray_menu.append_items(&[&open_i]).ok(); + } let tooltip = |count: usize| { if count == 0 { format!( @@ -155,15 +168,19 @@ fn make_tray() -> hbb_common::ResultType<()> { } if let Ok(event) = menu_channel.try_recv() { - if event.id == quit_i.id() { - /* failed in windows, seems no permission to check system process - if !crate::check_process("--server", false) { - *control_flow = ControlFlow::Exit; - return; - } - */ - if !crate::platform::uninstall_service(false, false) { - *control_flow = ControlFlow::Exit; + if let Some(quit_i) = &quit_i { + if event.id == quit_i.id() { + /* failed in windows, seems no permission to check system process + if !crate::check_process("--server", false) { + *control_flow = ControlFlow::Exit; + return; + } + */ + if !crate::platform::uninstall_service(false, false) { + *control_flow = ControlFlow::Exit; + } + } else if event.id == open_i.id() { + open_func(); } } else if event.id == open_i.id() { open_func(); diff --git a/src/ui/index.tis b/src/ui/index.tis index 5853fe3e2..acec6a2b5 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -16,6 +16,7 @@ const disable_ab = handler.is_disable_ab(); const hide_server_settings = handler.get_builtin_option("hide-server-settings") == "Y"; const hide_proxy_settings = handler.get_builtin_option("hide-proxy-settings") == "Y"; const hide_websocket_settings = handler.get_builtin_option("hide-websocket-settings") == "Y"; +const hide_stop_service = handler.get_builtin_option("hide-stop-service") == "Y"; const disable_change_permanent_password = handler.get_builtin_option("disable-change-permanent-password") == "Y"; const disable_change_id = handler.get_builtin_option("disable-change-id") == "Y"; @@ -532,7 +533,7 @@ class MyIdMenu: Reactor.Component { {!disable_settings && !using_public_server && !outgoing_only &&
  • {svg_checkmark}{translate('Disable UDP')}
  • } {!disable_settings && !using_public_server &&
  • {svg_checkmark}{translate('Allow insecure TLS fallback')}
  • }
    -
  • {svg_checkmark}{translate("Enable service")}
  • + {(!hide_stop_service || service_stopped) &&
  • {svg_checkmark}{translate("Enable service")}
  • } {!disable_settings && is_win && handler.is_installed() ? : ""} {!disable_settings && } {!disable_settings && false && handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • }