diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index a2a4e2b23..0cb2741be 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -302,6 +302,53 @@ Future changeDirectAccessPort( return controller.text; } +Future changeAutoDisconnectTimeout(String old) async { + final controller = TextEditingController(text: old); + await gFFI.dialogManager.show((setState, close, context) { + return CustomAlertDialog( + title: Text(translate("Timeout in minutes")), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8.0), + Row( + children: [ + Expanded( + child: TextField( + maxLines: null, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: '10', + isCollapsed: true, + suffix: IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.clear, size: 16), + onPressed: () => controller.clear())), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')), + ], + controller: controller, + autofocus: true), + ), + ], + ), + ], + ), + actions: [ + dialogButton("Cancel", onPressed: close, isOutline: true), + dialogButton("OK", onPressed: () async { + await bind.mainSetOption( + key: 'auto-disconnect-timeout', value: controller.text); + close(); + }), + ], + onCancel: close, + ); + }); + return controller.text; +} + class DialogTextField extends StatelessWidget { final String title; final String? hintText; diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 2da6ab1e7..2f5180bf6 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -728,6 +728,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { reverse: true, enabled: enabled), ...directIp(context), whitelist(), + ...autoDisconnect(context), ]); } @@ -906,6 +907,63 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { )); })); } + + List autoDisconnect(BuildContext context) { + TextEditingController controller = TextEditingController(); + update() => setState(() {}); + RxBool applyEnabled = false.obs; + final optionKey = 'allow-auto-disconnect'; + final timeoutKey = 'auto-disconnect-timeout'; + return [ + _OptionCheckBox(context, 'auto_disconnect_option_tip', optionKey, + update: update, enabled: !locked), + () { + bool enabled = + option2bool(optionKey, bind.mainGetOptionSync(key: optionKey)); + if (!enabled) applyEnabled.value = false; + controller.text = bind.mainGetOptionSync(key: timeoutKey); + return Offstage( + offstage: !enabled, + child: _SubLabeledWidget( + context, + 'Timeout in minutes', + Row(children: [ + SizedBox( + width: 95, + child: TextField( + controller: controller, + enabled: enabled && !locked, + onChanged: (_) => applyEnabled.value = true, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')), + ], + decoration: const InputDecoration( + hintText: '10', + contentPadding: + EdgeInsets.symmetric(vertical: 12, horizontal: 12), + ), + ).marginOnly(right: 15), + ), + Obx(() => ElevatedButton( + onPressed: applyEnabled.value && enabled && !locked + ? () async { + applyEnabled.value = false; + await bind.mainSetOption( + key: timeoutKey, value: controller.text); + } + : null, + child: Text( + translate('Apply'), + ), + )) + ]), + enabled: enabled && !locked, + ), + ); + }(), + ]; + } } class _Network extends StatefulWidget { diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 1cdfa9609..87d2eb8a1 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -45,10 +45,12 @@ class _SettingsState extends State with WidgetsBindingObserver { var _enableDirectIPAccess = false; var _enableRecordSession = false; var _autoRecordIncomingSession = false; + var _allowAutoDisconnect = false; var _localIP = ""; var _directAccessPort = ""; var _fingerprint = ""; var _buildDate = ""; + var _autoDisconnectTimeout = ""; @override void initState() { @@ -151,6 +153,20 @@ class _SettingsState extends State with WidgetsBindingObserver { _buildDate = buildDate; } + final allowAutoDisconnect = option2bool('allow-auto-disconnect', + await bind.mainGetOption(key: 'allow-auto-disconnect')); + if (allowAutoDisconnect != _allowAutoDisconnect) { + update = true; + _allowAutoDisconnect = allowAutoDisconnect; + } + + final autoDisconnectTimeout = + await bind.mainGetOption(key: 'auto-disconnect-timeout'); + if (autoDisconnectTimeout != _autoDisconnectTimeout) { + update = true; + _autoDisconnectTimeout = autoDisconnectTimeout; + } + if (update) { setState(() {}); } @@ -306,6 +322,48 @@ class _SettingsState extends State with WidgetsBindingObserver { await bind.mainSetOption(key: 'direct-server', value: value); setState(() {}); }, + ), + SettingsTile.switchTile( + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(translate("auto_disconnect_option_tip")), + Offstage( + offstage: !_allowAutoDisconnect, + child: Text( + '${_autoDisconnectTimeout.isEmpty ? '10' : _autoDisconnectTimeout} min', + style: Theme.of(context).textTheme.bodySmall, + )), + ])), + Offstage( + offstage: !_allowAutoDisconnect, + child: IconButton( + padding: EdgeInsets.zero, + icon: Icon( + Icons.edit, + size: 20, + ), + onPressed: () async { + final timeout = await changeAutoDisconnectTimeout( + _autoDisconnectTimeout); + setState(() { + _autoDisconnectTimeout = timeout; + }); + })) + ]), + initialValue: _allowAutoDisconnect, + onToggle: (_) async { + _allowAutoDisconnect = !_allowAutoDisconnect; + String value = + bool2option('allow-auto-disconnect', _allowAutoDisconnect); + await bind.mainSetOption(key: 'allow-auto-disconnect', value: value); + setState(() {}); + }, ) ]; if (_hasIgnoreBattery) { diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 52f859c75..c65adef0a 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 2abbde617..1d01bea6b 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 4fd3db18a..1a10b8bd1 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", "超时(分钟)"), + ("auto_disconnect_option_tip", "自动关闭不活跃的会话"), + ("Connection failed due to inactivity", "由于长时间无操作, 连接被自动断开"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 90a1482fb..457dae517 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 7af1c2282..6ea628cee 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index d72f51a06..cb44d7935 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 177d731b2..710751331 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 3384f7e8a..825698385 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -88,5 +88,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", "Don't show again"), ("I Agree", "I Agree"), ("Decline", "Decline"), + ("auto_disconnect_option_tip", "Automatically close incoming sessions on user inactivity"), + ("Connection failed due to inactivity", "Automatically disconnected due to inactivity"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index ecf1fe00f..7df5c3fa7 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 36b909f8f..1053dcf73 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 39169d816..1fd0be295 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index ea9dcef50..565bd5d42 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 62a41b96a..df19e7bbe 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 35da2fd89..1d4b3b4cf 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index f27e5ec51..33acdbe7d 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index af74fc4ed..1ecb8d9e2 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 7fe3da853..ad05b326f 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index eb8b7df61..e3fec6c5e 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 53ce2a23f..4c9cae2b7 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index d74957aea..43f277a0a 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 58650a593..ec370706e 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 18736a1bc..72a16d62b 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index b77862054..f11435d67 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 8be7a1600..d4cccc796 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index d4effc6d6..6be1274fd 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index f7620c7c7..0b58db74d 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 20b6cc819..2c82b35da 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 809b55a2a..32dc09f05 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 4b927e917..43ead1444 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index b048b9c8e..867fa9fcc 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 08f7254c9..397d6d380 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -549,7 +549,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("scam_text1", ""), ("scam_text2", ""), ("Don't show again", ""), - ("I Agree",""), - ("Decline",""), + ("I Agree", ""), + ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index c1e963461..c5e4d5fbb 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index ecf9ea5d9..28b8d385a 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index e55552f8e..104370258 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index aaee1a82b..7147445e0 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 2116400ab..9b910955e 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -551,5 +551,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Don't show again", ""), ("I Agree", ""), ("Decline", ""), + ("Timeout in minutes", ""), + ("auto_disconnect_option_tip", ""), + ("Connection failed due to inactivity", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 8ba8fdc15..1c5e45d08 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -204,6 +204,7 @@ pub struct Connection { delay_response_instant: Instant, #[cfg(not(any(target_os = "android", target_os = "ios")))] start_cm_ipc_para: Option, + auto_disconnect_timer: Option<(Instant, u64)>, } impl ConnInner { @@ -343,6 +344,7 @@ impl Connection { rx_desktop_ready, tx_cm_stream_ready, }), + auto_disconnect_timer: None, }; let addr = hbb_common::try_into_v4(addr); if !conn.on_open(addr).await { @@ -605,6 +607,13 @@ impl Connection { _ = second_timer.tick() => { #[cfg(windows)] conn.portable_check(); + if let Some((instant, minute)) = conn.auto_disconnect_timer.as_ref() { + if instant.elapsed().as_secs() > minute * 60 { + conn.send_close_reason_no_retry("Connection failed due to inactivity").await; + conn.on_close("auto disconnect", true).await; + break; + } + } } _ = test_delay_timer.tick() => { if last_recv_time.elapsed() >= SEC30 { @@ -1139,6 +1148,7 @@ impl Connection { let mut s = s.write().unwrap(); #[cfg(not(any(target_os = "android", target_os = "ios")))] let _h = try_start_record_cursor_pos(); + self.auto_disconnect_timer = Self::get_auto_disconenct_timer(); s.add_connection(self.inner.clone(), &noperms); } } @@ -1612,6 +1622,7 @@ impl Connection { } self.input_mouse(me, self.inner.id()); } + self.update_auto_disconnect_timer(); } Some(message::Union::PointerDeviceEvent(pde)) => { #[cfg(any(target_os = "android", target_os = "ios"))] @@ -1647,6 +1658,7 @@ impl Connection { MOUSE_MOVE_TIME.store(get_time(), Ordering::SeqCst); self.input_pointer(pde, self.inner.id()); } + self.update_auto_disconnect_timer(); } #[cfg(any(target_os = "android", target_os = "ios"))] Some(message::Union::KeyEvent(..)) => {} @@ -1702,6 +1714,7 @@ impl Connection { self.input_key(me, false); } } + self.update_auto_disconnect_timer(); } Some(message::Union::Clipboard(_cb)) => { @@ -1890,6 +1903,7 @@ impl Connection { Some(misc::Union::ChatMessage(c)) => { self.send_to_cm(ipc::Data::ChatMessage { text: c.text }); self.chat_unanswered = true; + self.update_auto_disconnect_timer(); } Some(misc::Union::Option(o)) => { self.update_options(&o).await; @@ -1898,6 +1912,7 @@ impl Connection { if r { super::video_service::refresh(); } + self.update_auto_disconnect_timer(); } Some(misc::Union::VideoReceived(_)) => { video_service::notify_video_frame_fetched( @@ -2027,6 +2042,7 @@ impl Connection { let mut msg = Message::new(); msg.set_misc(misc); self.send(msg).await; + self.update_auto_disconnect_timer(); } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -2384,6 +2400,26 @@ impl Connection { } self.pressed_modifiers.clear(); } + + fn get_auto_disconenct_timer() -> Option<(Instant, u64)> { + if Config::get_option("allow-auto-disconnect") == "Y" { + let mut minute: u64 = Config::get_option("auto-disconnect-timeout") + .parse() + .unwrap_or(10); + if minute == 0 { + minute = 10; + } + Some((Instant::now(), minute)) + } else { + None + } + } + + fn update_auto_disconnect_timer(&mut self) { + self.auto_disconnect_timer + .as_mut() + .map(|t| t.0 = Instant::now()); + } } pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {