Merge branch 'rustdesk:master' into master

This commit is contained in:
Asura 2022-07-04 17:15:43 +08:00 committed by GitHub
commit 082f851d4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 239 additions and 86 deletions

View File

@ -43,7 +43,7 @@
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode|navigation"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"

View File

@ -310,19 +310,19 @@ class ImageModel with ChangeNotifier {
}
double get maxScale {
if (_image == null) return 1.0;
if (_image == null) return 1.5;
final size = MediaQueryData.fromWindow(ui.window).size;
final xscale = size.width / _image!.width;
final yscale = size.height / _image!.height;
return max(1.0, max(xscale, yscale));
return max(1.5, max(xscale, yscale));
}
double get minScale {
if (_image == null) return 1.0;
if (_image == null) return 1.5;
final size = MediaQueryData.fromWindow(ui.window).size;
final xscale = size.width / _image!.width;
final yscale = size.height / _image!.height;
return min(xscale, yscale);
return min(xscale, yscale) / 1.5;
}
}
@ -724,13 +724,17 @@ class FFI {
static void inputKey(String name, {bool? down, bool? press}) {
if (!ffiModel.keyboard()) return;
setByName(
'input_key',
json.encode(modify({
'name': name,
'down': (down ?? false).toString(),
'press': (press ?? true).toString()
})));
final Map<String, String> out = Map();
out['name'] = name;
// default: down = false
if (down == true) {
out['down'] = "true";
}
// default: press = true
if (press != false) {
out['press'] = "true";
}
setByName('input_key', json.encode(modify(out)));
}
static void moveMouse(double x, double y) {

View File

@ -343,9 +343,14 @@ class _RemotePageState extends State<RemotePage> {
onKey: (data, e) {
final key = e.logicalKey;
if (e is RawKeyDownEvent) {
if (e.repeat) {
if (e.repeat &&
!e.isAltPressed &&
!e.isControlPressed &&
!e.isShiftPressed &&
!e.isMetaPressed) {
sendRawKey(e, press: true);
} else {
sendRawKey(e, down: true);
if (e.isAltPressed && !FFI.alt) {
FFI.alt = true;
} else if (e.isControlPressed && !FFI.ctrl) {
@ -355,7 +360,6 @@ class _RemotePageState extends State<RemotePage> {
} else if (e.isMetaPressed && !FFI.command) {
FFI.command = true;
}
sendRawKey(e, down: true);
}
}
// [!_showEdit] workaround for soft-keyboard's control_key like Backspace / Enter
@ -481,6 +485,7 @@ class _RemotePageState extends State<RemotePage> {
/// DoubleFiner -> right click
/// HoldDrag -> left drag
Offset _cacheLongPressPosition = Offset(0, 0);
Widget getBodyForMobileWithGesture() {
final touchMode = FFI.ffiModel.touchMode;
return getMixinGestureDetector(
@ -504,10 +509,14 @@ class _RemotePageState extends State<RemotePage> {
},
onLongPressDown: (d) {
if (touchMode) {
FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
_cacheLongPressPosition = d.localPosition;
}
},
onLongPress: () {
if (touchMode) {
FFI.cursorModel
.move(_cacheLongPressPosition.dx, _cacheLongPressPosition.dy);
}
FFI.tap(MouseButtons.right);
},
onDoubleFinerTap: (d) {
@ -534,6 +543,15 @@ class _RemotePageState extends State<RemotePage> {
if (touchMode) {
FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
FFI.sendMouse('down', MouseButtons.left);
} else {
final cursorX = FFI.cursorModel.x;
final cursorY = FFI.cursorModel.y;
final visible =
FFI.cursorModel.getVisibleRect().inflate(1); // extend edges
final size = MediaQueryData.fromWindow(ui.window).size;
if (!visible.contains(Offset(cursorX, cursorY))) {
FFI.cursorModel.move(size.width / 2, size.height / 2);
}
}
},
onOneFingerPanUpdate: (d) {

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
enum CustomTouchGestureState {
enum GestureState {
none,
oneFingerPan,
twoFingerScale,
@ -35,64 +35,41 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate;
GestureDragEndCallback? onThreeFingerVerticalDragEnd;
var _currentState = CustomTouchGestureState.none;
Timer? _startEventDebounceTimer;
var _currentState = GestureState.none;
Timer? _debounceTimer;
void _init() {
debugPrint("CustomTouchGestureRecognizer init");
onStart = (d) {
_startEventDebounceTimer?.cancel();
if (d.pointerCount == 1) {
_currentState = CustomTouchGestureState.oneFingerPan;
if (onOneFingerPanStart != null) {
onOneFingerPanStart!(DragStartDetails(
localPosition: d.localFocalPoint, globalPosition: d.focalPoint));
}
debugPrint("start oneFingerPan");
} else if (d.pointerCount == 2) {
if (_currentState == CustomTouchGestureState.threeFingerVerticalDrag) {
// 3 -> 2 debounce
_startEventDebounceTimer = Timer(Duration(milliseconds: 200), () {
_currentState = CustomTouchGestureState.twoFingerScale;
if (onTwoFingerScaleStart != null) {
onTwoFingerScaleStart!(ScaleStartDetails(
localFocalPoint: d.localFocalPoint,
focalPoint: d.focalPoint));
}
debugPrint("debounce start twoFingerScale success");
});
}
_currentState = CustomTouchGestureState.twoFingerScale;
// startWatchTimer();
if (onTwoFingerScaleStart != null) {
onTwoFingerScaleStart!(ScaleStartDetails(
localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint));
}
debugPrint("start twoFingerScale");
} else if (d.pointerCount == 3) {
_currentState = CustomTouchGestureState.threeFingerVerticalDrag;
// onStart = (d) {};
onUpdate = (d) {
_debounceTimer?.cancel();
if (d.pointerCount == 1 && _currentState != GestureState.oneFingerPan) {
onOneFingerStartDebounce(d);
} else if (d.pointerCount == 2 &&
_currentState != GestureState.twoFingerScale) {
onTwoFingerStartDebounce(d);
} else if (d.pointerCount == 3 &&
_currentState != GestureState.threeFingerVerticalDrag) {
_currentState = GestureState.threeFingerVerticalDrag;
if (onThreeFingerVerticalDragStart != null) {
onThreeFingerVerticalDragStart!(
DragStartDetails(globalPosition: d.localFocalPoint));
}
debugPrint("start threeFingerScale");
// _reset();
}
};
onUpdate = (d) {
if (_currentState != CustomTouchGestureState.none) {
if (_currentState != GestureState.none) {
switch (_currentState) {
case CustomTouchGestureState.oneFingerPan:
case GestureState.oneFingerPan:
if (onOneFingerPanUpdate != null) {
onOneFingerPanUpdate!(_getDragUpdateDetails(d));
}
break;
case CustomTouchGestureState.twoFingerScale:
case GestureState.twoFingerScale:
if (onTwoFingerScaleUpdate != null) {
onTwoFingerScaleUpdate!(d);
}
break;
case CustomTouchGestureState.threeFingerVerticalDrag:
case GestureState.threeFingerVerticalDrag:
if (onThreeFingerVerticalDragUpdate != null) {
onThreeFingerVerticalDragUpdate!(_getDragUpdateDetails(d));
}
@ -105,21 +82,22 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
};
onEnd = (d) {
debugPrint("ScaleGestureRecognizer onEnd");
_debounceTimer?.cancel();
// end
switch (_currentState) {
case CustomTouchGestureState.oneFingerPan:
case GestureState.oneFingerPan:
debugPrint("TwoFingerState.pan onEnd");
if (onOneFingerPanEnd != null) {
onOneFingerPanEnd!(_getDragEndDetails(d));
}
break;
case CustomTouchGestureState.twoFingerScale:
case GestureState.twoFingerScale:
debugPrint("TwoFingerState.scale onEnd");
if (onTwoFingerScaleEnd != null) {
onTwoFingerScaleEnd!(d);
}
break;
case CustomTouchGestureState.threeFingerVerticalDrag:
case GestureState.threeFingerVerticalDrag:
debugPrint("ThreeFingerState.vertical onEnd");
if (onThreeFingerVerticalDragEnd != null) {
onThreeFingerVerticalDragEnd!(_getDragEndDetails(d));
@ -128,10 +106,50 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
default:
break;
}
_currentState = CustomTouchGestureState.none;
_debounceTimer = Timer(Duration(milliseconds: 200), () {
_currentState = GestureState.none;
});
};
}
void onOneFingerStartDebounce(ScaleUpdateDetails d) {
final start = (ScaleUpdateDetails d) {
_currentState = GestureState.oneFingerPan;
if (onOneFingerPanStart != null) {
onOneFingerPanStart!(DragStartDetails(
localPosition: d.localFocalPoint, globalPosition: d.focalPoint));
}
};
if (_currentState != GestureState.none) {
_debounceTimer = Timer(Duration(milliseconds: 200), () {
start(d);
debugPrint("debounce start oneFingerPan");
});
} else {
start(d);
debugPrint("start oneFingerPan");
}
}
void onTwoFingerStartDebounce(ScaleUpdateDetails d) {
final start = (ScaleUpdateDetails d) {
_currentState = GestureState.twoFingerScale;
if (onTwoFingerScaleStart != null) {
onTwoFingerScaleStart!(ScaleStartDetails(
localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint));
}
};
if (_currentState == GestureState.threeFingerVerticalDrag) {
_debounceTimer = Timer(Duration(milliseconds: 200), () {
start(d);
debugPrint("debounce start twoFingerScale");
});
} else {
start(d);
debugPrint("start twoFingerScale");
}
}
DragUpdateDetails _getDragUpdateDetails(ScaleUpdateDetails d) =>
DragUpdateDetails(
globalPosition: d.focalPoint,

View File

@ -47,7 +47,7 @@ case "$1" in
;;
2)
# for upgrade
service rustdesk stop || true
systemctl stop rustdesk || true
;;
esac
@ -61,10 +61,26 @@ systemctl start rustdesk
update-desktop-database
%preun
systemctl stop rustdesk || true
systemctl disable rustdesk || true
rm /etc/systemd/system/rustdesk.service || true
case "$1" in
0)
# for uninstall
systemctl stop rustdesk || true
systemctl disable rustdesk || true
rm /etc/systemd/system/rustdesk.service || true
;;
1)
# for upgrade
;;
esac
%postun
rm /usr/share/applications/rustdesk.desktop || true
update-desktop-database
case "$1" in
0)
# for uninstall
rm /usr/share/applications/rustdesk.desktop || true
update-desktop-database
;;
1)
# for upgrade
;;
esac

View File

@ -48,7 +48,7 @@ case "$1" in
;;
2)
# for upgrade
service rustdesk stop || true
systemctl stop rustdesk || true
;;
esac
@ -62,10 +62,26 @@ systemctl start rustdesk
update-desktop-database
%preun
systemctl stop rustdesk || true
systemctl disable rustdesk || true
rm /etc/systemd/system/rustdesk.service || true
case "$1" in
0)
# for uninstall
systemctl stop rustdesk || true
systemctl disable rustdesk || true
rm /etc/systemd/system/rustdesk.service || true
;;
1)
# for upgrade
;;
esac
%postun
rm /usr/share/applications/rustdesk.desktop || true
update-desktop-database
case "$1" in
0)
# for uninstall
rm /usr/share/applications/rustdesk.desktop || true
update-desktop-database
;;
1)
# for upgrade
;;
esac

View File

@ -1,21 +1,43 @@
use serde_json::{json, value::Value};
use std::ops::Deref;
mod cn;
mod cs;
mod da;
mod sk;
mod de;
mod en;
mod es;
mod eo;
mod es;
mod fr;
mod id;
mod it;
mod ptbr;
mod ru;
mod sk;
mod tr;
mod tw;
lazy_static::lazy_static! {
pub static ref LANGS: Value =
json!(vec![
("en", "English"),
("it", "Italiano"),
("fr", "Français"),
("de", "Deutsch"),
("cn", "简体中文"),
("tw", "繁體中文"),
("pt", "Português"),
("es", "Español"),
("ru", "Русский"),
("sk", "Slovenčina"),
("id", "Indonesia"),
("cs", "Čeština"),
("da", "Dansk"),
("eo", "Esperanto"),
("tr", "Türkçe"),
]);
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn translate(name: String) -> String {
let locale = sys_locale::get_locale().unwrap_or_default().to_lowercase();

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "退出"),
("In privacy mode", "进入隐私模式"),
("Out privacy mode", "退出隐私模式"),
("Language", "语言"),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Vypnutý"),
("In privacy mode", "v režimu soukromí"),
("Out privacy mode", "mimo režim soukromí"),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Slukket"),
("In privacy mode", "I databeskyttelsestilstand"),
("Out privacy mode", "Databeskyttelsestilstand fra"),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Ausgeschaltet"),
("In privacy mode", "im Datenschutzmodus"),
("Out privacy mode", "Datenschutzmodus aus"),
("Language", "Sprache"),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", ""),
("In privacy mode", ""),
("Out privacy mode", ""),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Apagado"),
("In privacy mode", "En modo de privacidad"),
("Out privacy mode", "Fuera del modo de privacidad"),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Éteindre"),
("In privacy mode", "en mode privé"),
("Out privacy mode", "hors mode de confidentialité"),
("Language", "Langue"),
].iter().cloned().collect();
}

View File

@ -158,7 +158,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using clipboard", "Izinkan menggunakan papan klip"),
("Allow hearing sound", "Izinkan mendengarkan suara"),
("Allow file copy and paste", "Izinkan penyalinan dan tempel file"),
("File transfer", "Transfer file"),
("Connected", "Terkoneksi"),
("Direct and encrypted connection", "Koneksi langsung dan terenkripsi"),
("Relayed and encrypted connection", "Koneksi relai dan terenkripsi"),
@ -280,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Matikan"),
("In privacy mode", "Dalam mode privasi"),
("Out privacy mode", "Keluar dari mode privasi"),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Spegni"),
("In privacy mode", "In modalità privacy"),
("Out privacy mode", "Fuori modalità privacy"),
("Language", "Linguaggio"),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Desligado"),
("In privacy mode", "No modo de privacidade"),
("Out privacy mode", "Fora do modo de privacidade"),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -263,9 +263,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_version_audio_tip", "Текущая версия Android не поддерживает захват звука, обновите ее до Android 10 или выше."),
("android_start_service_tip", "Нажмите [Запуск промежуточного сервера] или ОТКРЫТЬ разрешение [Захват экрана], чтобы запустить службу демонстрации экрана."),
("Account", "Аккаунт"),
("Quit", "Выйти"),
("Overwrite", "Перезаписать"),
("This file exists, skip or overwrite this file?", "Этот файл существует, пропустить или перезаписать этот файл?"),
("Quit", "Выйти"),
("doc_mac_permission", "https://rustdesk.com/docs/ru/manual/mac/#включение-разрешений"),
("Help", "Помощь"),
("Failed", "Неуспешный"),
@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Выключен"),
("In privacy mode", "В режиме конфиденциальности"),
("Out privacy mode", "Выход из режима конфиденциальности"),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Vypnutý"),
("In privacy mode", "V režime súkromia"),
("Out privacy mode", "Mimo režimu súkromia"),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -264,7 +264,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_start_service_tip", ""),
("Account", ""),
("Overwrite", ""),
("This file exists, skip or overwrite this file?", "")
("This file exists, skip or overwrite this file?", ""),
("Quit", ""),
("doc_mac_permission", ""),
("Help", ""),
@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", ""),
("In privacy mode", ""),
("Out privacy mode", ""),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -279,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "Kapalı"),
("In privacy mode", "Gizlilik modunda"),
("Out privacy mode", "Gizlilik modu dışında"),
("Language", ""),
].iter().cloned().collect();
}

View File

@ -262,13 +262,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_stop_service_tip", "關閉服務將自動關閉所有已建立的連接。"),
("android_version_audio_tip", "目前的 Android 版本不支持音訊錄製,請升級至 Android 10 或以上版本。"),
("android_start_service_tip", "點擊 「啟動服務」 或啟用 「畫面錄製」 權限以開啟手機畫面共享服務。"),
("Account", "帳號"),
("Quit", "退出"),
("Account", "帳戶"),
("Overwrite", "覆寫"),
("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要跳過或是覆寫此檔案嗎?"),
("Quit", "退出"),
("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"),
("Help", "幫助"),
("Account", "帳戶"),
("Failed", "失敗"),
("Succeeded", "成功"),
("Someone turns on privacy mode, exit", "其他用戶開啟隱私模式,退出"),
@ -280,5 +279,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Turned off", "退出"),
("In privacy mode", "開啟隱私模式"),
("Out privacy mode", "退出隱私模式"),
("Language", "語言"),
].iter().cloned().collect();
}

View File

@ -1059,6 +1059,7 @@ sc start {app_name}
sc stop {app_name}
sc delete {app_name}
{after_install}
{sleep}
",
uninstall_str=uninstall_str,
path=path,
@ -1081,6 +1082,11 @@ sc delete {app_name}
config_path=Config::file().to_str().unwrap_or(""),
lic=register_licence(),
after_install=get_after_install(&exe),
sleep=if debug {
"timeout 300"
} else {
""
}
);
run_cmds(cmds, debug, "install")?;
std::thread::sleep(std::time::Duration::from_millis(2000));

View File

@ -3,9 +3,9 @@ mod cm;
mod inline;
#[cfg(target_os = "macos")]
mod macos;
pub mod remote;
#[cfg(target_os = "windows")]
pub mod win_privacy;
pub mod remote;
use crate::common::SOFTWARE_UPDATE_URL;
use crate::ipc;
use hbb_common::{
@ -702,7 +702,7 @@ impl UI {
let p = "explorer";
#[cfg(target_os = "macos")]
let p = "open";
#[cfg(target_os = "linux")]
#[cfg(target_os = "linux")]
let p = if std::path::Path::new("/usr/bin/firefox").exists() {
"firefox"
} else {
@ -753,6 +753,10 @@ impl UI {
self.get_option_("custom-rendezvous-server"),
)
}
fn get_langs(&self) -> String {
crate::lang::LANGS.to_string()
}
}
impl sciter::EventHandler for UI {
@ -829,6 +833,7 @@ impl sciter::EventHandler for UI {
fn discover();
fn get_lan_peers();
fn get_uuid();
fn get_langs();
}
}

View File

@ -163,8 +163,43 @@ class AudioInputs: Reactor.Component {
}
this.toggleMenuState();
}
};
class Languages: Reactor.Component {
function render() {
var langs = JSON.parse(handler.get_langs());
var me = this;
self.timer(1ms, function() { me.toggleMenuState() });
return <li>{translate('Language')}
<menu #languages key={langs.length}>
<li id="default"><span>{svg_checkmark}</span>Default</li>
<div .separator />
{langs.map(function(lang) {
return <li id={lang[0]}><span>{svg_checkmark}</span>{lang[1]}</li>;
})}
</menu>
</li>;
}
function toggleMenuState() {
var cur = handler.get_local_option("lang") || "default";
for (var el in this.$$(menu#languages>li)) {
var selected = cur == el.id;
el.attributes.toggleClass("selected", selected);
}
}
event click $(menu#languages>li) (_, me) {
var v = me.id;
if (v == "default") v = "";
handler.set_local_option("lang", v);
app.update();
this.toggleMenuState();
}
}
function getUserName() {
try {
return JSON.parse(handler.get_local_option("user_info")).name;
@ -222,7 +257,7 @@ class MyIdMenu: Reactor.Component {
{handler.is_ok_change_id() && key_confirmed ? <li #change-id>{translate('Change ID')}</li> : ""}
<div .separator />
<li #allow-darktheme><span>{svg_checkmark}</span>{translate('Dark Theme')}</li>
<div .separator />
<Languages />
<li #about>{translate('About')} {" "}{handler.get_app_name()}</li>
</menu>
</popup>;

View File

@ -1956,11 +1956,11 @@ impl Remote {
let mut config: PeerConfig = self.handler.load_config();
let mut transfer_metas = TransferSerde::default();
for job in self.read_jobs.iter() {
let json_str = serde_json::to_string(&job.gen_meta()).unwrap();
let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default();
transfer_metas.read_jobs.push(json_str);
}
for job in self.write_jobs.iter() {
let json_str = serde_json::to_string(&job.gen_meta()).unwrap();
let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default();
transfer_metas.write_jobs.push(json_str);
}
log::info!("meta: {:?}", transfer_metas);
@ -2371,7 +2371,7 @@ impl Remote {
}
back_notification::PrivacyModeState::OffSucceeded => {
self.handler
.msgbox("custom-nocancel", "Privacy mode", "Out privacy mode");
.msgbox("custom-nocancel", "Privacy mode", "Out privacy mode");
self.update_privacy_mode(false);
}
back_notification::PrivacyModeState::OffByPeer => {