Fix/custom client advanced settings (#8066)

* fix: custom client, advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: custom client, default options

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: cargo test

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: remove prefix $ and unify option keys

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* refact: custom client, advanced options

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* debug custom client, advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* custom client, advanced settings. Add filter-transfer to display settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* custom client, advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: custom client, advanced settings, codec

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* fix: custom client, advanced settings, whitelist

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou
2024-05-17 14:19:11 +08:00
committed by GitHub
parent 3a4390e0c7
commit 8357d4675a
18 changed files with 707 additions and 364 deletions

View File

@@ -550,7 +550,8 @@ class MyTheme {
Get.changeThemeMode(mode);
if (desktopType == DesktopType.main || isAndroid || isIOS) {
if (mode == ThemeMode.system) {
await bind.mainSetLocalOption(key: kCommConfKeyTheme, value: '');
await bind.mainSetLocalOption(
key: kCommConfKeyTheme, value: defaultOptionTheme);
} else {
await bind.mainSetLocalOption(
key: kCommConfKeyTheme, value: mode.toShortString());
@@ -1421,13 +1422,13 @@ bool option2bool(String option, String value) {
String bool2option(String option, bool b) {
String res;
if (option.startsWith('enable-')) {
res = b ? '' : 'N';
res = b ? defaultOptionYes : 'N';
} else if (option.startsWith('allow-') ||
option == "stop-service" ||
option == "direct-server" ||
option == "stop-rendezvous-service" ||
option == kOptionForceAlwaysRelay) {
res = b ? 'Y' : '';
res = b ? 'Y' : defaultOptionNo;
} else {
assert(false);
res = b ? 'Y' : 'N';
@@ -3281,3 +3282,14 @@ setResizable(bool resizable) {
windowManager.setResizable(resizable);
}
}
isOptionFixed(String key) => bind.mainIsOptionFixed(key: key);
final isCustomClient = bind.isCustomClient();
get defaultOptionLang => isCustomClient ? 'default' : '';
get defaultOptionTheme => isCustomClient ? 'system' : '';
get defaultOptionYes => isCustomClient ? 'Y' : '';
get defaultOptionNo => isCustomClient ? 'N' : '';
get defaultOptionWhitelist => isCustomClient ? ',' : '';
get defaultOptionAccessMode => isCustomClient ? 'custom' : '';
get defaultOptionApproveMode => isCustomClient ? 'password-click' : '';

View File

@@ -333,6 +333,7 @@ class _AddressBookState extends State<AddressBook> {
@protected
MenuEntryBase<String> syncMenuItem() {
final isOptFixed = isOptionFixed(syncAbOption);
return MenuEntrySwitch<String>(
switchType: SwitchType.scheckbox,
text: translate('Sync with recent sessions'),
@@ -343,11 +344,13 @@ class _AddressBookState extends State<AddressBook> {
gFFI.abModel.setShouldAsync(v);
},
dismissOnClicked: true,
enabled: (!isOptFixed).obs,
);
}
@protected
MenuEntryBase<String> sortMenuItem() {
final isOptFixed = isOptionFixed(sortAbTagsOption);
return MenuEntrySwitch<String>(
switchType: SwitchType.scheckbox,
text: translate('Sort tags'),
@@ -355,15 +358,17 @@ class _AddressBookState extends State<AddressBook> {
return shouldSortTags();
},
setter: (bool v) async {
bind.mainSetLocalOption(key: sortAbTagsOption, value: v ? 'Y' : '');
bind.mainSetLocalOption(key: sortAbTagsOption, value: v ? 'Y' : defaultOptionNo);
gFFI.abModel.sortTags.value = v;
},
dismissOnClicked: true,
enabled: (!isOptFixed).obs,
);
}
@protected
MenuEntryBase<String> filterMenuItem() {
final isOptFixed = isOptionFixed(filterAbTagOption);
return MenuEntrySwitch<String>(
switchType: SwitchType.scheckbox,
text: translate('Filter by intersection'),
@@ -371,10 +376,11 @@ class _AddressBookState extends State<AddressBook> {
return filterAbTagByIntersection();
},
setter: (bool v) async {
bind.mainSetLocalOption(key: filterAbTagOption, value: v ? 'Y' : '');
bind.mainSetLocalOption(key: filterAbTagOption, value: v ? 'Y' : defaultOptionNo);
gFFI.abModel.filterByIntersection.value = v;
},
dismissOnClicked: true,
enabled: (!isOptFixed).obs,
);
}

View File

@@ -177,11 +177,14 @@ void changeIdDialog() {
}
void changeWhiteList({Function()? callback}) async {
var newWhiteList = (await bind.mainGetOption(key: 'whitelist')).split(',');
var newWhiteListField = newWhiteList.join('\n');
final curWhiteList = await bind.mainGetOption(key: kOptionWhitelist);
var newWhiteListField = curWhiteList == defaultOptionWhitelist
? ''
: curWhiteList.split(',').join('\n');
var controller = TextEditingController(text: newWhiteListField);
var msg = "";
var isInProgress = false;
final isOptFixed = isOptionFixed(kOptionWhitelist);
gFFI.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate("IP Whitelisting")),
@@ -214,14 +217,15 @@ void changeWhiteList({Function()? callback}) async {
),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("Clear", onPressed: () async {
await bind.mainSetOption(key: 'whitelist', value: '');
dialogButton("Clear", onPressed: isOptFixed ? null : () async {
await bind.mainSetOption(
key: kOptionWhitelist, value: defaultOptionWhitelist);
callback?.call();
close();
}, isOutline: true),
dialogButton(
"OK",
onPressed: () async {
onPressed: isOptFixed ? null : () async {
setState(() {
msg = "";
isInProgress = true;
@@ -248,7 +252,11 @@ void changeWhiteList({Function()? callback}) async {
}
newWhiteList = ips.join(',');
}
await bind.mainSetOption(key: 'whitelist', value: newWhiteList);
if (newWhiteList.trim().isEmpty) {
newWhiteList = defaultOptionWhitelist;
}
await bind.mainSetOption(
key: kOptionWhitelist, value: newWhiteList);
callback?.call();
close();
},
@@ -298,7 +306,7 @@ Future<String> changeDirectAccessPort(
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("OK", onPressed: () async {
await bind.mainSetOption(
key: 'direct-access-port', value: controller.text);
key: kOptionDirectAccessPort, value: controller.text);
close();
}),
],
@@ -345,7 +353,7 @@ Future<String> changeAutoDisconnectTimeout(String old) async {
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("OK", onPressed: () async {
await bind.mainSetOption(
key: 'auto-disconnect-timeout', value: controller.text);
key: kOptionAutoDisconnectTimeout, value: controller.text);
close();
}),
],

View File

@@ -537,7 +537,7 @@ class _PeerTabPageState extends State<PeerTabPage>
),
onTap: () async {
await bind.mainSetLocalOption(
key: "hideAbTagsPanel", value: hideAbTagsPanel.value ? "" : "Y");
key: "hideAbTagsPanel", value: hideAbTagsPanel.value ? defaultOptionNo : "Y");
hideAbTagsPanel.value = !hideAbTagsPanel.value;
});
}

View File

@@ -10,8 +10,8 @@ import 'package:get/get.dart';
customImageQualityWidget(
{required double initQuality,
required double initFps,
required Function(double) setQuality,
required Function(double) setFps,
required Function(double)? setQuality,
required Function(double)? setFps,
required bool showFps,
required bool showMoreQuality}) {
if (initQuality < kMinQuality ||
@@ -27,16 +27,12 @@ customImageQualityWidget(
final RxBool moreQualityChecked = RxBool(qualityValue.value > kMaxQuality);
final debouncerQuality = Debouncer<double>(
Duration(milliseconds: 1000),
onChanged: (double v) {
setQuality(v);
},
onChanged: setQuality,
initialValue: qualityValue.value,
);
final debouncerFps = Debouncer<double>(
Duration(milliseconds: 1000),
onChanged: (double v) {
setFps(v);
},
onChanged: setFps,
initialValue: fpsValue.value,
);
@@ -62,10 +58,12 @@ customImageQualityWidget(
divisions: moreQualityChecked.value
? ((kMaxMoreQuality - kMinQuality) / 10).round()
: ((kMaxQuality - kMinQuality) / 5).round(),
onChanged: (double value) async {
qualityValue.value = value;
debouncerQuality.value = value;
},
onChanged: setQuality == null
? null
: (double value) async {
qualityValue.value = value;
debouncerQuality.value = value;
},
),
),
Expanded(
@@ -124,10 +122,12 @@ customImageQualityWidget(
min: kMinFps,
max: kMaxFps,
divisions: ((kMaxFps - kMinFps) / 5).round(),
onChanged: (double value) async {
fpsValue.value = value;
debouncerFps.value = value;
},
onChanged: setFps == null
? null
: (double value) async {
fpsValue.value = value;
debouncerFps.value = value;
},
),
),
Expanded(
@@ -152,21 +152,29 @@ customImageQualitySetting() {
final qualityKey = 'custom_image_quality';
final fpsKey = 'custom-fps';
var initQuality =
final initQuality =
(double.tryParse(bind.mainGetUserDefaultOption(key: qualityKey)) ??
kDefaultQuality);
var initFps = (double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ??
kDefaultFps);
final isQuanlityFixed = isOptionFixed(qualityKey);
final initFps =
(double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ??
kDefaultFps);
final isFpsFixed = isOptionFixed(fpsKey);
return customImageQualityWidget(
initQuality: initQuality,
initFps: initFps,
setQuality: (v) {
bind.mainSetUserDefaultOption(key: qualityKey, value: v.toString());
},
setFps: (v) {
bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString());
},
setQuality: isQuanlityFixed
? null
: (v) {
bind.mainSetUserDefaultOption(
key: qualityKey, value: v.toString());
},
setFps: isFpsFixed
? null
: (v) {
bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString());
},
showFps: true,
showMoreQuality: true);
}
@@ -208,23 +216,25 @@ List<Widget> ServerConfigImportExportWidgets(
List<(String, String)> otherDefaultSettings() {
List<(String, String)> v = [
('View Mode', 'view_only'),
if ((isDesktop || isWebDesktop)) ('show_monitors_tip', kKeyShowMonitorsToolbar),
if ((isDesktop || isWebDesktop)) ('Collapse toolbar', 'collapse_toolbar'),
('Show remote cursor', 'show_remote_cursor'),
('Follow remote cursor', 'follow_remote_cursor'),
('Follow remote window focus', 'follow_remote_window'),
if ((isDesktop || isWebDesktop)) ('Zoom cursor', 'zoom-cursor'),
('Show quality monitor', 'show_quality_monitor'),
('Mute', 'disable_audio'),
if (isDesktop) ('Enable file copy and paste', 'enable_file_transfer'),
('Disable clipboard', 'disable_clipboard'),
('Lock after session end', 'lock_after_session_end'),
('Privacy mode', 'privacy_mode'),
if (isMobile) ('Touch mode', 'touch-mode'),
('True color (4:4:4)', 'i444'),
('View Mode', kOptionViewOnly),
if ((isDesktop || isWebDesktop))
('show_monitors_tip', kKeyShowMonitorsToolbar),
if ((isDesktop || isWebDesktop))
('Collapse toolbar', kOptionCollapseToolbar),
('Show remote cursor', kOptionShowRemoteCursor),
('Follow remote cursor', kOptionFollowRemoteCursor),
('Follow remote window focus', kOptionFollowRemoteWindow),
if ((isDesktop || isWebDesktop)) ('Zoom cursor', kOptionZoomCursor),
('Show quality monitor', kOptionShowQualityMonitor),
('Mute', kOptionDisableAudio),
if (isDesktop) ('Enable file copy and paste', kOptionEnableFileTransfer),
('Disable clipboard', kOptionDisableClipboard),
('Lock after session end', kOptionLockAfterSessionEnd),
('Privacy mode', kOptionPrivacyMode),
if (isMobile) ('Touch mode', kOptionTouchMode),
('True color (4:4:4)', kOptionI444),
('Reverse mouse wheel', kKeyReverseMouseWheel),
('swap-left-right-mouse', 'swap-left-right-mouse'),
('swap-left-right-mouse', kOptionSwapLeftRightMouse),
if (isDesktop && useTextureRender)
(
'Show displays as individual windows',

View File

@@ -327,7 +327,7 @@ Future<List<TRadioMenu<String>>> toolbarCodec(
final alternativeCodecs =
await bind.sessionAlternativeCodecs(sessionId: sessionId);
final groupValue = await bind.sessionGetOption(
sessionId: sessionId, arg: 'codec-preference') ??
sessionId: sessionId, arg: kOptionCodecPreference) ??
'';
final List<bool> codecs = [];
try {
@@ -349,7 +349,7 @@ Future<List<TRadioMenu<String>>> toolbarCodec(
onChanged(String? value) async {
if (value == null) return;
await bind.sessionPeerOption(
sessionId: sessionId, name: 'codec-preference', value: value);
sessionId: sessionId, name: kOptionCodecPreference, value: value);
bind.sessionChangePreferCodec(sessionId: sessionId);
}

View File

@@ -68,11 +68,53 @@ const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window";
const String kWindowEventGetCachedSessionData = "get_cached_session_data";
const String kWindowEventOpenMonitorSession = "open_monitor_session";
const String kOptionViewStyle = "view_style";
const String kOptionScrollStyle = "scroll_style";
const String kOptionImageQuality = "image_quality";
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
const String kOptionOpenInTabs = "allow-open-in-tabs";
const String kOptionOpenInWindows = "allow-open-in-windows";
const String kOptionForceAlwaysRelay = "force-always-relay";
const String kOptionViewOnly = "view-only";
const String kOptionViewOnly = "view_only";
const String kOptionEnableLanDiscovery = "enable-lan-discovery";
const String kOptionWhitelist = "whitelist";
const String kOptionEnableAbr = "enable-abr";
const String kOptionEnableRecordSession = "enable-record-session";
const String kOptionDirectServer = "direct-server";
const String kOptionDirectAccessPort = "direct-access-port";
const String kOptionAllowAutoDisconnect = "allow-auto-disconnect";
const String kOptionAutoDisconnectTimeout = "auto-disconnect-timeout";
const String kOptionEnableHwcodec = "enable-hwcodec";
const String kOptionAllowAutoRecordIncoming = "allow-auto-record-incoming";
const String kOptionVideoSaveDirectory = "video-save-directory";
const String kOptionAccessMode = "access-mode";
const String kOptionEnableKeyboard = "enable-keyboard";
// "Settings -> Security -> Permissions"
const String kOptionEnableClipboard = "enable-clipboard";
const String kOptionEnableFileTransfer = "enable-file-transfer";
const String kOptionEnableAudio = "enable-audio";
const String kOptionEnableTunnel = "enable-tunnel";
const String kOptionEnableRemoteRestart = "enable-remote-restart";
const String kOptionEnableBlockInput = "enable-block-input";
const String kOptionAllowRemoteConfigModification =
"allow-remote-config-modification";
const String kOptionVerificationMethod = "verification-method";
const String kOptionApproveMode = "approve-mode";
const String kOptionCollapseToolbar = "collapse_toolbar";
const String kOptionShowRemoteCursor = "show_remote_cursor";
const String kOptionFollowRemoteCursor = "follow_remote_cursor";
const String kOptionFollowRemoteWindow = "follow_remote_window";
const String kOptionZoomCursor = "zoom-cursor";
const String kOptionShowQualityMonitor = "show_quality_monitor";
const String kOptionDisableAudio = "disable_audio";
// "Settings -> Display -> Other default options"
const String kOptionDisableClipboard = "disable_clipboard";
const String kOptionLockAfterSessionEnd = "lock_after_session_end";
const String kOptionPrivacyMode = "privacy_mode";
const String kOptionTouchMode = "touch-mode";
const String kOptionI444 = "i444";
const String kOptionSwapLeftRightMouse = 'swap-left-right-mouse';
const String kOptionCodecPreference = 'codec-preference';
const String kUrlActionClose = "close";
@@ -208,12 +250,6 @@ const kRemoteImageQualityLow = 'low';
/// [kRemoteImageQualityCustom] Custom image quality.
const kRemoteImageQualityCustom = 'custom';
/// [kRemoteAudioGuestToHost] Guest to host audio mode(default).
const kRemoteAudioGuestToHost = 'guest-to-host';
/// [kRemoteAudioDualWay] dual-way audio mode(default).
const kRemoteAudioDualWay = 'dual-way';
const kIgnoreDpi = true;
// ================================ mobile ================================

View File

@@ -332,22 +332,23 @@ class _GeneralState extends State<_General> {
setState(() {});
}
final isOptFixed = isOptionFixed(kCommConfKeyTheme);
return _Card(title: 'Theme', children: [
_Radio<String>(context,
value: 'light',
groupValue: current,
label: 'Light',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio<String>(context,
value: 'dark',
groupValue: current,
label: 'Dark',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio<String>(context,
value: 'system',
groupValue: current,
label: 'Follow System',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
]);
}
@@ -547,21 +548,24 @@ class _GeneralState extends State<_General> {
)).marginOnly(left: 10),
),
ElevatedButton(
onPressed: () async {
String? initialDirectory;
if (await Directory.fromUri(Uri.directory(user_dir))
.exists()) {
initialDirectory = user_dir;
}
String? selectedDirectory = await FilePicker.platform
.getDirectoryPath(initialDirectory: initialDirectory);
if (selectedDirectory != null) {
await bind.mainSetOption(
key: 'video-save-directory',
value: selectedDirectory);
setState(() {});
}
},
onPressed: isOptionFixed(kOptionVideoSaveDirectory)
? null
: () async {
String? initialDirectory;
if (await Directory.fromUri(Uri.directory(user_dir))
.exists()) {
initialDirectory = user_dir;
}
String? selectedDirectory =
await FilePicker.platform.getDirectoryPath(
initialDirectory: initialDirectory);
if (selectedDirectory != null) {
await bind.mainSetOption(
key: kOptionVideoSaveDirectory,
value: selectedDirectory);
setState(() {});
}
},
child: Text(translate('Change')))
.marginOnly(left: 5),
],
@@ -580,12 +584,13 @@ class _GeneralState extends State<_General> {
Map<String, String> langsMap = {for (var v in langsList) v[0]: v[1]};
List<String> keys = langsMap.keys.toList();
List<String> values = langsMap.values.toList();
keys.insert(0, '');
keys.insert(0, defaultOptionLang);
values.insert(0, translate('Default'));
String currentKey = bind.mainGetLocalOption(key: kCommConfKeyLang);
if (!keys.contains(currentKey)) {
currentKey = '';
currentKey = defaultOptionLang;
}
final isOptFixed = isOptionFixed(kCommConfKeyLang);
return ComboBox(
keys: keys,
values: values,
@@ -595,6 +600,7 @@ class _GeneralState extends State<_General> {
reloadAllWindows();
bind.mainChangeLanguage(lang: key);
},
enabled: !isOptFixed,
).marginOnly(left: _kContentHMargin);
});
}
@@ -728,7 +734,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
return _Card(title: 'Permissions', children: [
ComboBox(
keys: [
'',
defaultOptionAccessMode,
'full',
'view',
],
@@ -737,37 +743,39 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
translate('Full Access'),
translate('Screen Share'),
],
enabled: enabled,
enabled: enabled && !isOptionFixed(kOptionAccessMode),
initialKey: initialKey,
onChanged: (mode) async {
await bind.mainSetOption(key: 'access-mode', value: mode);
await bind.mainSetOption(key: kOptionAccessMode, value: mode);
setState(() {});
}).marginOnly(left: _kContentHMargin),
Column(
children: [
_OptionCheckBox(context, 'Enable keyboard/mouse', 'enable-keyboard',
_OptionCheckBox(
context, 'Enable keyboard/mouse', kOptionEnableKeyboard,
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(context, 'Enable clipboard', 'enable-clipboard',
_OptionCheckBox(context, 'Enable clipboard', kOptionEnableClipboard,
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(
context, 'Enable file transfer', 'enable-file-transfer',
context, 'Enable file transfer', kOptionEnableFileTransfer,
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(context, 'Enable audio', 'enable-audio',
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(context, 'Enable TCP tunneling', 'enable-tunnel',
_OptionCheckBox(context, 'Enable audio', kOptionEnableAudio,
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(
context, 'Enable remote restart', 'enable-remote-restart',
context, 'Enable TCP tunneling', kOptionEnableTunnel,
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(
context, 'Enable recording session', 'enable-record-session',
context, 'Enable remote restart', kOptionEnableRemoteRestart,
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(
context, 'Enable recording session', kOptionEnableRecordSession,
enabled: enabled, fakeValue: fakeValue),
if (isWindows)
_OptionCheckBox(
context, 'Enable blocking user input', 'enable-block-input',
_OptionCheckBox(context, 'Enable blocking user input',
kOptionEnableBlockInput,
enabled: enabled, fakeValue: fakeValue),
_OptionCheckBox(context, 'Enable remote configuration modification',
'allow-remote-config-modification',
kOptionAllowRemoteConfigModification,
enabled: enabled, fakeValue: fakeValue),
],
),
@@ -801,14 +809,15 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
value: value,
groupValue: currentValue,
label: value,
onChanged: ((value) {
() async {
await model.setVerificationMethod(
passwordKeys[passwordValues.indexOf(value)]);
await model.updatePasswordModel();
}();
}),
enabled: !locked,
onChanged: locked
? null
: ((value) {
() async {
await model.setVerificationMethod(
passwordKeys[passwordValues.indexOf(value)]);
await model.updatePasswordModel();
}();
}),
))
.toList();
@@ -842,7 +851,11 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
))
.toList();
final modeKeys = ['password', 'click', ''];
final modeKeys = <String>[
'password',
'click',
defaultOptionApproveMode
];
final modeValues = [
translate('Accept sessions via password'),
translate('Accept sessions via click'),
@@ -852,9 +865,10 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
if (!modeKeys.contains(modeInitialKey)) modeInitialKey = '';
final usePassword = model.approveMode != 'click';
final isApproveModeFixed = isOptionFixed(kOptionApproveMode);
return _Card(title: 'Password', children: [
ComboBox(
enabled: !locked,
enabled: !locked && !isApproveModeFixed,
keys: modeKeys,
values: modeValues,
initialKey: modeInitialKey,
@@ -930,15 +944,17 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
update() => setState(() {});
RxBool applyEnabled = false.obs;
return [
_OptionCheckBox(context, 'Enable direct IP access', 'direct-server',
_OptionCheckBox(context, 'Enable direct IP access', kOptionDirectServer,
update: update, enabled: !locked),
() {
// Simple temp wrapper for PR check
tmpWrapper() {
bool enabled = option2bool(
'direct-server', bind.mainGetOptionSync(key: 'direct-server'));
bool enabled = option2bool(kOptionDirectServer,
bind.mainGetOptionSync(key: kOptionDirectServer));
if (!enabled) applyEnabled.value = false;
controller.text = bind.mainGetOptionSync(key: 'direct-access-port');
controller.text =
bind.mainGetOptionSync(key: kOptionDirectAccessPort);
final isOptFixed = isOptionFixed(kOptionDirectAccessPort);
return Offstage(
offstage: !enabled,
child: _SubLabeledWidget(
@@ -949,7 +965,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
width: 95,
child: TextField(
controller: controller,
enabled: enabled && !locked,
enabled: enabled && !locked && !isOptFixed,
onChanged: (_) => applyEnabled.value = true,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(
@@ -963,11 +979,14 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
).marginOnly(right: 15),
),
Obx(() => ElevatedButton(
onPressed: applyEnabled.value && enabled && !locked
onPressed: applyEnabled.value &&
enabled &&
!locked &&
!isOptFixed
? () async {
applyEnabled.value = false;
await bind.mainSetOption(
key: 'direct-access-port',
key: kOptionDirectAccessPort,
value: controller.text);
}
: null,
@@ -976,7 +995,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
),
))
]),
enabled: enabled && !locked,
enabled: enabled && !locked && !isOptFixed,
),
);
}
@@ -990,17 +1009,19 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
bool enabled = !locked;
// Simple temp wrapper for PR check
tmpWrapper() {
RxBool hasWhitelist =
bind.mainGetOptionSync(key: 'whitelist').isNotEmpty.obs;
RxBool hasWhitelist = (bind.mainGetOptionSync(key: kOptionWhitelist) !=
defaultOptionWhitelist)
.obs;
update() async {
hasWhitelist.value =
bind.mainGetOptionSync(key: 'whitelist').isNotEmpty;
hasWhitelist.value = bind.mainGetOptionSync(key: kOptionWhitelist) !=
defaultOptionWhitelist;
}
onChanged(bool? checked) async {
changeWhiteList(callback: update);
}
final isOptFixed = isOptionFixed(kOptionWhitelist);
return GestureDetector(
child: Tooltip(
message: translate('whitelist_tip'),
@@ -1008,13 +1029,16 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
children: [
Checkbox(
value: hasWhitelist.value,
onChanged: enabled ? onChanged : null)
onChanged: enabled && !isOptFixed ? onChanged : null)
.marginOnly(right: 5),
Offstage(
offstage: !hasWhitelist.value,
child: const Icon(Icons.warning_amber_rounded,
color: Color.fromARGB(255, 255, 204, 0))
.marginOnly(right: 5),
child: MouseRegion(
child: const Icon(Icons.warning_amber_rounded,
color: Color.fromARGB(255, 255, 204, 0))
.marginOnly(right: 5),
cursor: SystemMouseCursors.click,
),
),
Expanded(
child: Text(
@@ -1025,9 +1049,11 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
],
)),
),
onTap: () {
onChanged(!hasWhitelist.value);
},
onTap: enabled
? () {
onChanged(!hasWhitelist.value);
}
: null,
).marginOnly(left: _kCheckBoxLeftMargin);
}
@@ -1078,16 +1104,17 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
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,
_OptionCheckBox(
context, 'auto_disconnect_option_tip', kOptionAllowAutoDisconnect,
update: update, enabled: !locked),
() {
bool enabled =
option2bool(optionKey, bind.mainGetOptionSync(key: optionKey));
bool enabled = option2bool(kOptionAllowAutoDisconnect,
bind.mainGetOptionSync(key: kOptionAllowAutoDisconnect));
if (!enabled) applyEnabled.value = false;
controller.text = bind.mainGetOptionSync(key: timeoutKey);
controller.text =
bind.mainGetOptionSync(key: kOptionAutoDisconnectTimeout);
final isOptFixed = isOptionFixed(kOptionAutoDisconnectTimeout);
return Offstage(
offstage: !enabled,
child: _SubLabeledWidget(
@@ -1098,7 +1125,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
width: 95,
child: TextField(
controller: controller,
enabled: enabled && !locked,
enabled: enabled && !locked && isOptFixed,
onChanged: (_) => applyEnabled.value = true,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(
@@ -1112,19 +1139,21 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
).marginOnly(right: 15),
),
Obx(() => ElevatedButton(
onPressed: applyEnabled.value && enabled && !locked
? () async {
applyEnabled.value = false;
await bind.mainSetOption(
key: timeoutKey, value: controller.text);
}
: null,
onPressed:
applyEnabled.value && enabled && !locked && !isOptFixed
? () async {
applyEnabled.value = false;
await bind.mainSetOption(
key: kOptionAutoDisconnectTimeout,
value: controller.text);
}
: null,
child: Text(
translate('Apply'),
),
))
]),
enabled: enabled && !locked,
enabled: enabled && !locked && !isOptFixed,
),
);
}(),
@@ -1273,46 +1302,47 @@ class _DisplayState extends State<_Display> {
}
Widget viewStyle(BuildContext context) {
final key = 'view_style';
final isOptFixed = isOptionFixed(kOptionViewStyle);
onChanged(String value) async {
await bind.mainSetUserDefaultOption(key: key, value: value);
await bind.mainSetUserDefaultOption(key: kOptionViewStyle, value: value);
setState(() {});
}
final groupValue = bind.mainGetUserDefaultOption(key: key);
final groupValue = bind.mainGetUserDefaultOption(key: kOptionViewStyle);
return _Card(title: 'Default View Style', children: [
_Radio(context,
value: kRemoteViewStyleOriginal,
groupValue: groupValue,
label: 'Scale original',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio(context,
value: kRemoteViewStyleAdaptive,
groupValue: groupValue,
label: 'Scale adaptive',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
]);
}
Widget scrollStyle(BuildContext context) {
final key = 'scroll_style';
final isOptFixed = isOptionFixed(kOptionScrollStyle);
onChanged(String value) async {
await bind.mainSetUserDefaultOption(key: key, value: value);
await bind.mainSetUserDefaultOption(
key: kOptionScrollStyle, value: value);
setState(() {});
}
final groupValue = bind.mainGetUserDefaultOption(key: key);
final groupValue = bind.mainGetUserDefaultOption(key: kOptionScrollStyle);
return _Card(title: 'Default Scroll Style', children: [
_Radio(context,
value: kRemoteScrollStyleAuto,
groupValue: groupValue,
label: 'ScrollAuto',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio(context,
value: kRemoteScrollStyleBar,
groupValue: groupValue,
label: 'Scrollbar',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
]);
}
@@ -1323,28 +1353,29 @@ class _DisplayState extends State<_Display> {
setState(() {});
}
final isOptFixed = isOptionFixed(key);
final groupValue = bind.mainGetUserDefaultOption(key: key);
return _Card(title: 'Default Image Quality', children: [
_Radio(context,
value: kRemoteImageQualityBest,
groupValue: groupValue,
label: 'Good image quality',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio(context,
value: kRemoteImageQualityBalanced,
groupValue: groupValue,
label: 'Balanced',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio(context,
value: kRemoteImageQualityLow,
groupValue: groupValue,
label: 'Optimize reaction time',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio(context,
value: kRemoteImageQualityCustom,
groupValue: groupValue,
label: 'Custom',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
Offstage(
offstage: groupValue != kRemoteImageQualityCustom,
child: customImageQualitySetting(),
@@ -1353,14 +1384,16 @@ class _DisplayState extends State<_Display> {
}
Widget codec(BuildContext context) {
final key = 'codec-preference';
onChanged(String value) async {
await bind.mainSetUserDefaultOption(key: key, value: value);
await bind.mainSetUserDefaultOption(
key: kOptionCodecPreference, value: value);
setState(() {});
}
final groupValue = bind.mainGetUserDefaultOption(key: key);
final groupValue =
bind.mainGetUserDefaultOption(key: kOptionCodecPreference);
var hwRadios = [];
final isOptFixed = isOptionFixed(kOptionCodecPreference);
try {
final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings());
final h264 = codecsJson['h264'] ?? false;
@@ -1370,14 +1403,14 @@ class _DisplayState extends State<_Display> {
value: 'h264',
groupValue: groupValue,
label: 'H264',
onChanged: onChanged));
onChanged: isOptFixed ? null : onChanged));
}
if (h265) {
hwRadios.add(_Radio(context,
value: 'h265',
groupValue: groupValue,
label: 'H265',
onChanged: onChanged));
onChanged: isOptFixed ? null : onChanged));
}
} catch (e) {
debugPrint("failed to parse supported hwdecodings, err=$e");
@@ -1387,22 +1420,22 @@ class _DisplayState extends State<_Display> {
value: 'auto',
groupValue: groupValue,
label: 'Auto',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio(context,
value: 'vp8',
groupValue: groupValue,
label: 'VP8',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio(context,
value: 'vp9',
groupValue: groupValue,
label: 'VP9',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
_Radio(context,
value: 'av1',
groupValue: groupValue,
label: 'AV1',
onChanged: onChanged),
onChanged: isOptFixed ? null : onChanged),
...hwRadios,
]);
}
@@ -1445,22 +1478,29 @@ class _DisplayState extends State<_Display> {
Widget otherRow(String label, String key) {
final value = bind.mainGetUserDefaultOption(key: key) == 'Y';
final isOptFixed = isOptionFixed(key);
onChanged(bool b) async {
await bind.mainSetUserDefaultOption(key: key, value: b ? 'Y' : '');
await bind.mainSetUserDefaultOption(
key: key,
value: b
? 'Y'
: (key == kOptionEnableFileTransfer ? 'N' : defaultOptionNo));
setState(() {});
}
return GestureDetector(
child: Row(
children: [
Checkbox(value: value, onChanged: (_) => onChanged(!value))
Checkbox(
value: value,
onChanged: isOptFixed ? null : (_) => onChanged(!value))
.marginOnly(right: 5),
Expanded(
child: Text(translate(label)),
)
],
).marginOnly(left: _kCheckBoxLeftMargin),
onTap: () => onChanged(!value));
onTap: isOptFixed ? null : () => onChanged(!value));
}
Widget other(BuildContext context) {
@@ -1772,6 +1812,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
bool isServer = true}) {
bool value =
isServer ? mainGetBoolOptionSync(key) : mainGetLocalBoolOptionSync(key);
final isOptFixed = isOptionFixed(key);
if (reverse) value = !value;
var ref = value.obs;
onChanged(option) async {
@@ -1801,7 +1842,9 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
child: Obx(
() => Row(
children: [
Checkbox(value: ref.value, onChanged: enabled ? onChanged : null)
Checkbox(
value: ref.value,
onChanged: enabled && !isOptFixed ? onChanged : null)
.marginOnly(right: 5),
Offstage(
offstage: !ref.value || checkedIcon == null,
@@ -1815,7 +1858,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
],
),
).marginOnly(left: _kCheckBoxLeftMargin),
onTap: enabled
onTap: enabled && !isOptFixed
? () {
onChanged(!ref.value);
}
@@ -1828,10 +1871,9 @@ Widget _Radio<T>(BuildContext context,
{required T value,
required T groupValue,
required String label,
required Function(T value) onChanged,
bool autoNewLine = true,
bool enabled = true}) {
var onChange = enabled
required Function(T value)? onChanged,
bool autoNewLine = true}) {
final onChange2 = onChanged != null
? (T? value) {
if (value != null) {
onChanged(value);
@@ -1841,18 +1883,18 @@ Widget _Radio<T>(BuildContext context,
return GestureDetector(
child: Row(
children: [
Radio<T>(value: value, groupValue: groupValue, onChanged: onChange),
Radio<T>(value: value, groupValue: groupValue, onChanged: onChange2),
Expanded(
child: Text(translate(label),
overflow: autoNewLine ? null : TextOverflow.ellipsis,
style: TextStyle(
fontSize: _kContentFontSize,
color: disabledTextColor(context, enabled)))
color: disabledTextColor(context, onChange2 != null)))
.marginOnly(left: 5),
),
],
).marginOnly(left: _kRadioLeftMargin),
onTap: () => onChange?.call(value),
onTap: () => onChange2?.call(value),
);
}

View File

@@ -428,7 +428,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
}
ConnectionTypeState.init(id);
_toolbarState.setShow(
bind.mainGetUserDefaultOption(key: 'collapse_toolbar') != 'Y');
bind.mainGetUserDefaultOption(key: kOptionCollapseToolbar) != 'Y');
tabController.add(TabInfo(
key: id,
label: id,

View File

@@ -54,7 +54,7 @@ class ToolbarState {
_initSet(bool s, bool p) {
// Show remubar when connection is established.
show =
RxBool(bind.mainGetUserDefaultOption(key: 'collapse_toolbar') != 'Y');
RxBool(bind.mainGetUserDefaultOption(key: kOptionCollapseToolbar) != 'Y');
_pin = RxBool(p);
}

View File

@@ -39,6 +39,7 @@ class ServerPage extends StatefulWidget implements PageShape {
final approveMode = gFFI.serverModel.approveMode;
final verificationMethod = gFFI.serverModel.verificationMethod;
final showPasswordOption = approveMode != 'click';
final isApproveModeFixed = isOptionFixed(kOptionApproveMode);
return [
PopupMenuItem(
enabled: gFFI.serverModel.connectStatus > 0,
@@ -50,16 +51,19 @@ class ServerPage extends StatefulWidget implements PageShape {
value: 'AcceptSessionsViaPassword',
child: listTile(
'Accept sessions via password', approveMode == 'password'),
enabled: !isApproveModeFixed,
),
PopupMenuItem(
value: 'AcceptSessionsViaClick',
child:
listTile('Accept sessions via click', approveMode == 'click'),
enabled: !isApproveModeFixed,
),
PopupMenuItem(
value: "AcceptSessionsViaBoth",
child: listTile("Accept sessions via both",
approveMode != 'password' && approveMode != 'click'),
enabled: !isApproveModeFixed,
),
if (showPasswordOption) const PopupMenuDivider(),
if (showPasswordOption &&
@@ -116,7 +120,7 @@ class ServerPage extends StatefulWidget implements PageShape {
} else if (value == "Click") {
gFFI.serverModel.setApproveMode('click');
} else {
gFFI.serverModel.setApproveMode('');
gFFI.serverModel.setApproveMode(defaultOptionApproveMode);
}
}
})

View File

@@ -100,8 +100,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_denyLANDiscovery = denyLanDiscovery;
}
final onlyWhiteList =
(await bind.mainGetOption(key: 'whitelist')).isNotEmpty;
final onlyWhiteList = (await bind.mainGetOption(key: kOptionWhitelist)) !=
defaultOptionWhitelist;
if (onlyWhiteList != _onlyWhiteList) {
update = true;
_onlyWhiteList = onlyWhiteList;
@@ -143,7 +143,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
}
final directAccessPort =
await bind.mainGetOption(key: 'direct-access-port');
await bind.mainGetOption(key: kOptionDirectAccessPort);
if (directAccessPort != _directAccessPort) {
update = true;
_directAccessPort = directAccessPort;
@@ -257,16 +257,18 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
SettingsTile.switchTile(
title: Text(translate('Deny LAN discovery')),
initialValue: _denyLANDiscovery,
onToggle: (v) async {
await bind.mainSetOption(
key: "enable-lan-discovery",
value: bool2option("enable-lan-discovery", !v));
final newValue = !option2bool('enable-lan-discovery',
await bind.mainGetOption(key: 'enable-lan-discovery'));
setState(() {
_denyLANDiscovery = newValue;
});
},
onToggle: isOptionFixed(kOptionEnableLanDiscovery)
? null
: (v) async {
await bind.mainSetOption(
key: kOptionEnableLanDiscovery,
value: bool2option(kOptionEnableLanDiscovery, !v));
final newValue = !option2bool(kOptionEnableLanDiscovery,
await bind.mainGetOption(key: kOptionEnableLanDiscovery));
setState(() {
_denyLANDiscovery = newValue;
});
},
),
SettingsTile.switchTile(
title: Row(children: [
@@ -279,42 +281,51 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
]),
initialValue: _onlyWhiteList,
onToggle: (_) async {
update() async {
final onlyWhiteList =
(await bind.mainGetOption(key: 'whitelist')).isNotEmpty;
if (onlyWhiteList != _onlyWhiteList) {
setState(() {
_onlyWhiteList = onlyWhiteList;
});
}
}
update() async {
final onlyWhiteList =
(await bind.mainGetOption(key: kOptionWhitelist)) !=
defaultOptionWhitelist;
if (onlyWhiteList != _onlyWhiteList) {
setState(() {
_onlyWhiteList = onlyWhiteList;
});
}
}
changeWhiteList(callback: update);
},
changeWhiteList(callback: update);
},
),
SettingsTile.switchTile(
title: Text('${translate('Adaptive bitrate')} (beta)'),
initialValue: _enableAbr,
onToggle: (v) async {
await bind.mainSetOption(key: "enable-abr", value: v ? "" : "N");
final newValue = await bind.mainGetOption(key: "enable-abr") != "N";
setState(() {
_enableAbr = newValue;
});
},
onToggle: isOptionFixed(kOptionEnableAbr)
? null
: (v) async {
await bind.mainSetOption(
key: kOptionEnableAbr, value: v ? defaultOptionYes : "N");
final newValue =
await bind.mainGetOption(key: kOptionEnableAbr) != "N";
setState(() {
_enableAbr = newValue;
});
},
),
SettingsTile.switchTile(
title: Text(translate('Enable recording session')),
initialValue: _enableRecordSession,
onToggle: (v) async {
await bind.mainSetOption(
key: "enable-record-session", value: v ? "" : "N");
final newValue =
await bind.mainGetOption(key: "enable-record-session") != "N";
setState(() {
_enableRecordSession = newValue;
});
},
onToggle: isOptionFixed(kOptionEnableRecordSession)
? null
: (v) async {
await bind.mainSetOption(
key: kOptionEnableRecordSession,
value: v ? defaultOptionYes : "N");
final newValue =
await bind.mainGetOption(key: kOptionEnableRecordSession) !=
"N";
setState(() {
_enableRecordSession = newValue;
});
},
),
SettingsTile.switchTile(
title: Row(
@@ -341,21 +352,27 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
Icons.edit,
size: 20,
),
onPressed: () async {
final port = await changeDirectAccessPort(
_localIP, _directAccessPort);
setState(() {
_directAccessPort = port;
});
}))
onPressed: isOptionFixed(kOptionDirectAccessPort)
? null
: () async {
final port = await changeDirectAccessPort(
_localIP, _directAccessPort);
setState(() {
_directAccessPort = port;
});
}))
]),
initialValue: _enableDirectIPAccess,
onToggle: (_) async {
_enableDirectIPAccess = !_enableDirectIPAccess;
String value = bool2option('direct-server', _enableDirectIPAccess);
await bind.mainSetOption(key: 'direct-server', value: value);
setState(() {});
},
onToggle: isOptionFixed(kOptionDirectServer)
? null
: (_) async {
_enableDirectIPAccess = !_enableDirectIPAccess;
String value =
bool2option(kOptionDirectServer, _enableDirectIPAccess);
await bind.mainSetOption(
key: kOptionDirectServer, value: value);
setState(() {});
},
),
SettingsTile.switchTile(
title: Row(
@@ -382,22 +399,27 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
Icons.edit,
size: 20,
),
onPressed: () async {
final timeout = await changeAutoDisconnectTimeout(
_autoDisconnectTimeout);
setState(() {
_autoDisconnectTimeout = timeout;
});
}))
onPressed: isOptionFixed(kOptionAutoDisconnectTimeout)
? null
: () 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(() {});
},
onToggle: isOptionFixed(kOptionAllowAutoDisconnect)
? null
: (_) async {
_allowAutoDisconnect = !_allowAutoDisconnect;
String value = bool2option(
kOptionAllowAutoDisconnect, _allowAutoDisconnect);
await bind.mainSetOption(
key: kOptionAllowAutoDisconnect, value: value);
setState(() {});
},
)
];
if (_hasIgnoreBattery) {
@@ -526,15 +548,19 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
SettingsTile.switchTile(
title: Text(translate('Enable hardware codec')),
initialValue: _enableHardwareCodec,
onToggle: (v) async {
await bind.mainSetOption(
key: "enable-hwcodec", value: v ? "" : "N");
final newValue =
await bind.mainGetOption(key: "enable-hwcodec") != "N";
setState(() {
_enableHardwareCodec = newValue;
});
},
onToggle: isOptionFixed(kOptionEnableHwcodec)
? null
: (v) async {
await bind.mainSetOption(
key: kOptionEnableHwcodec,
value: v ? defaultOptionYes : "N");
final newValue =
await bind.mainGetOption(key: kOptionEnableHwcodec) !=
"N";
setState(() {
_enableHardwareCodec = newValue;
});
},
),
]),
if (isAndroid && !outgoingOnly)
@@ -551,18 +577,21 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
child: Text("${translate("Directory")}: ${data.data}")),
future: bind.mainVideoSaveDirectory(root: false)),
initialValue: _autoRecordIncomingSession,
onToggle: (v) async {
await bind.mainSetOption(
key: "allow-auto-record-incoming",
value: bool2option("allow-auto-record-incoming", v));
final newValue = option2bool(
'allow-auto-record-incoming',
await bind.mainGetOption(
key: 'allow-auto-record-incoming'));
setState(() {
_autoRecordIncomingSession = newValue;
});
},
onToggle: isOptionFixed(kOptionAllowAutoRecordIncoming)
? null
: (v) async {
await bind.mainSetOption(
key: kOptionAllowAutoRecordIncoming,
value:
bool2option(kOptionAllowAutoRecordIncoming, v));
final newValue = option2bool(
kOptionAllowAutoRecordIncoming,
await bind.mainGetOption(
key: kOptionAllowAutoRecordIncoming));
setState(() {
_autoRecordIncomingSession = newValue;
});
},
),
],
),
@@ -661,29 +690,32 @@ void showServerSettings(OverlayDialogManager dialogManager) async {
void showLanguageSettings(OverlayDialogManager dialogManager) async {
try {
final langs = json.decode(await bind.mainGetLangs()) as List<dynamic>;
var lang = bind.mainGetLocalOption(key: "lang");
var lang = bind.mainGetLocalOption(key: kCommConfKeyLang);
dialogManager.show((setState, close, context) {
setLang(v) async {
if (lang != v) {
setState(() {
lang = v;
});
await bind.mainSetLocalOption(key: "lang", value: v);
await bind.mainSetLocalOption(key: kCommConfKeyLang, value: v);
HomePage.homeKey.currentState?.refreshPages();
Future.delayed(Duration(milliseconds: 200), close);
}
}
final isOptFixed = isOptionFixed(kCommConfKeyLang);
return CustomAlertDialog(
content: Column(
children: [
getRadio(Text(translate('Default')), '', lang, setLang),
getRadio(Text(translate('Default')), defaultOptionLang, lang,
isOptFixed ? null : setLang),
Divider(color: MyTheme.border),
] +
langs.map((e) {
final key = e[0] as String;
final name = e[1] as String;
return getRadio(Text(translate(name)), key, lang, setLang);
return getRadio(Text(translate(name)), key, lang,
isOptFixed ? null : setLang);
}).toList(),
),
);
@@ -707,13 +739,15 @@ void showThemeSettings(OverlayDialogManager dialogManager) async {
}
}
final isOptFixed = isOptionFixed(kCommConfKeyTheme);
return CustomAlertDialog(
content: Column(children: [
getRadio(
Text(translate('Light')), ThemeMode.light, themeMode, setTheme),
getRadio(Text(translate('Dark')), ThemeMode.dark, themeMode, setTheme),
getRadio(Text(translate('Light')), ThemeMode.light, themeMode,
isOptFixed ? null : setTheme),
getRadio(Text(translate('Dark')), ThemeMode.dark, themeMode,
isOptFixed ? null : setTheme),
getRadio(Text(translate('Follow System')), ThemeMode.system, themeMode,
setTheme)
isOptFixed ? null : setTheme)
]),
);
}, backDismiss: true, clickMaskDismiss: true);
@@ -801,11 +835,14 @@ class __DisplayPageState extends State<_DisplayPage> {
_RadioEntry('Scale original', kRemoteViewStyleOriginal),
_RadioEntry('Scale adaptive', kRemoteViewStyleAdaptive)
],
getter: () => bind.mainGetUserDefaultOption(key: 'view_style'),
asyncSetter: (value) async {
await bind.mainSetUserDefaultOption(
key: 'view_style', value: value);
},
getter: () =>
bind.mainGetUserDefaultOption(key: kOptionViewStyle),
asyncSetter: isOptionFixed(kOptionViewStyle)
? null
: (value) async {
await bind.mainSetUserDefaultOption(
key: kOptionViewStyle, value: value);
},
),
_getPopupDialogRadioEntry(
title: 'Default Image Quality',
@@ -816,16 +853,19 @@ class __DisplayPageState extends State<_DisplayPage> {
_RadioEntry('Custom', kRemoteImageQualityCustom),
],
getter: () {
final v = bind.mainGetUserDefaultOption(key: 'image_quality');
final v =
bind.mainGetUserDefaultOption(key: kOptionImageQuality);
showCustomImageQuality.value = v == kRemoteImageQualityCustom;
return v;
},
asyncSetter: (value) async {
await bind.mainSetUserDefaultOption(
key: 'image_quality', value: value);
showCustomImageQuality.value =
value == kRemoteImageQualityCustom;
},
asyncSetter: isOptionFixed(kOptionImageQuality)
? null
: (value) async {
await bind.mainSetUserDefaultOption(
key: kOptionImageQuality, value: value);
showCustomImageQuality.value =
value == kRemoteImageQualityCustom;
},
tail: customImageQualitySetting(),
showTail: showCustomImageQuality,
notCloseValue: kRemoteImageQualityCustom,
@@ -834,11 +874,13 @@ class __DisplayPageState extends State<_DisplayPage> {
title: 'Default Codec',
list: codecList,
getter: () =>
bind.mainGetUserDefaultOption(key: 'codec-preference'),
asyncSetter: (value) async {
await bind.mainSetUserDefaultOption(
key: 'codec-preference', value: value);
},
bind.mainGetUserDefaultOption(key: kOptionCodecPreference),
asyncSetter: isOptionFixed(kOptionCodecPreference)
? null
: (value) async {
await bind.mainSetUserDefaultOption(
key: kOptionCodecPreference, value: value);
},
),
],
),
@@ -853,13 +895,17 @@ class __DisplayPageState extends State<_DisplayPage> {
SettingsTile otherRow(String label, String key) {
final value = bind.mainGetUserDefaultOption(key: key) == 'Y';
final isOptFixed = isOptionFixed(key);
return SettingsTile.switchTile(
initialValue: value,
title: Text(translate(label)),
onToggle: (b) async {
await bind.mainSetUserDefaultOption(key: key, value: b ? 'Y' : '');
setState(() {});
},
onToggle: isOptFixed
? null
: (b) async {
await bind.mainSetUserDefaultOption(
key: key, value: b ? 'Y' : defaultOptionNo);
setState(() {});
},
);
}
}
@@ -877,7 +923,7 @@ _getPopupDialogRadioEntry({
required String title,
required List<_RadioEntry> list,
required _RadioEntryGetter getter,
required _RadioEntrySetter asyncSetter,
required _RadioEntrySetter? asyncSetter,
Widget? tail,
RxBool? showTail,
String? notCloseValue,
@@ -897,21 +943,23 @@ _getPopupDialogRadioEntry({
void showDialog() async {
gFFI.dialogManager.show((setState, close, context) {
onChanged(String? value) async {
if (value == null) return;
await asyncSetter(value);
init();
if (value != notCloseValue) {
close();
}
}
final onChanged = asyncSetter == null
? null
: (String? value) async {
if (value == null) return;
await asyncSetter(value);
init();
if (value != notCloseValue) {
close();
}
};
return CustomAlertDialog(
content: Obx(
() => Column(children: [
...list
.map((e) => getRadio(Text(translate(e.label)), e.value,
groupValue.value, (String? value) => onChanged(value)))
groupValue.value, onChanged))
.toList(),
Offstage(
offstage:

View File

@@ -509,7 +509,7 @@ class AbModel {
}
void setShouldAsync(bool v) async {
await bind.mainSetLocalOption(key: syncAbOption, value: v ? 'Y' : '');
await bind.mainSetLocalOption(key: syncAbOption, value: v ? 'Y' : defaultOptionNo);
_syncAllFromRecent = true;
_timerCounter = 0;
}

View File

@@ -77,7 +77,7 @@ class ServerModel with ChangeNotifier {
String get approveMode => _approveMode;
setVerificationMethod(String method) async {
await bind.mainSetOption(key: "verification-method", value: method);
await bind.mainSetOption(key: kOptionVerificationMethod, value: method);
/*
if (method != kUsePermanentPassword) {
await bind.mainSetOption(
@@ -99,7 +99,7 @@ class ServerModel with ChangeNotifier {
}
setApproveMode(String mode) async {
await bind.mainSetOption(key: 'approve-mode', value: mode);
await bind.mainSetOption(key: kOptionApproveMode, value: mode);
/*
if (mode != 'password') {
await bind.mainSetOption(
@@ -283,7 +283,7 @@ class ServerModel with ChangeNotifier {
}
_audioOk = !_audioOk;
bind.mainSetOption(key: "enable-audio", value: _audioOk ? '' : 'N');
bind.mainSetOption(key: "enable-audio", value: _audioOk ? defaultOptionYes : 'N');
notifyListeners();
}
@@ -302,7 +302,7 @@ class ServerModel with ChangeNotifier {
}
_fileOk = !_fileOk;
bind.mainSetOption(key: "enable-file-transfer", value: _fileOk ? '' : 'N');
bind.mainSetOption(key: kOptionEnableFileTransfer, value: _fileOk ? defaultOptionYes : 'N');
notifyListeners();
}
@@ -312,7 +312,7 @@ class ServerModel with ChangeNotifier {
}
if (_inputOk) {
parent.target?.invokeMethod("stop_input");
bind.mainSetOption(key: "enable-keyboard", value: 'N');
bind.mainSetOption(key: kOptionEnableKeyboard, value: 'N');
} else {
if (parent.target != null) {
/// the result of toggle-on depends on user actions in the settings page.
@@ -445,7 +445,7 @@ class ServerModel with ChangeNotifier {
break;
case "input":
if (_inputOk != value) {
bind.mainSetOption(key: "enable-keyboard", value: value ? '' : 'N');
bind.mainSetOption(key: kOptionEnableKeyboard, value: value ? defaultOptionYes : 'N');
}
_inputOk = value;
break;

View File

@@ -1610,5 +1610,9 @@ class RustdeskImpl {
throw UnimplementedError();
}
bool mainIsOptionFixed({required String key, dynamic hint}) {
throw UnimplementedError();
}
void dispose() {}
}