diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index a2a4e2b2..0cb2741b 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 2da6ab1e..2f5180bf 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 1cdfa960..87d2eb8a 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 52f859c7..c65adef0 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 2abbde61..1d01bea6 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 4fd3db18..1a10b8bd 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 90a1482f..457dae51 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 7af1c228..6ea628ce 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 d72f51a0..cb44d793 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 177d731b..71075133 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 3384f7e8..82569838 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 ecf1fe00..7df5c3fa 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 36b909f8..1053dcf7 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 39169d81..1fd0be29 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 ea9dcef5..565bd5d4 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 62a41b96..df19e7bb 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 35da2fd8..1d4b3b4c 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 f27e5ec5..33acdbe7 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 af74fc4e..1ecb8d9e 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 7fe3da85..ad05b326 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 eb8b7df6..e3fec6c5 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 53ce2a23..4c9cae2b 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 d74957ae..43f277a0 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 58650a59..ec370706 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 18736a1b..72a16d62 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 b7786205..f11435d6 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 8be7a160..d4cccc79 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 d4effc6d..6be1274f 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 f7620c7c..0b58db74 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 20b6cc81..2c82b35d 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 809b55a2..32dc09f0 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 4b927e91..43ead144 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 b048b9c8..867fa9fc 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 08f7254c..397d6d38 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 c1e96346..c5e4d5fb 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 ecf9ea5d..28b8d385 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 e55552f8..10437025 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 aaee1a82..7147445e 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 2116400a..9b910955 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 add13e3d..2eebd54c 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) {