mobile add default display, merge set server and custom quality code

Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
21pages
2023-08-31 20:30:20 +08:00
parent b7145959a7
commit 54de5b0300
7 changed files with 583 additions and 365 deletions

View File

@@ -466,7 +466,7 @@ class _RemotePageState extends State<RemotePage> {
gFFI.ffiModel.toggleTouchMode();
final v = gFFI.ffiModel.touchMode ? 'Y' : '';
bind.sessionPeerOption(
sessionId: sessionId, name: "touch", value: v);
sessionId: sessionId, name: "touch-mode", value: v);
})));
}

View File

@@ -2,7 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:settings_ui/settings_ui.dart';
@@ -458,6 +458,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
title: Text(translate("Share Screen")),
tiles: shareScreenTiles,
),
defaultDisplaySection(),
if (isAndroid)
SettingsSection(
title: Text(translate("Enhancements")),
@@ -513,6 +514,23 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
}
return true;
}
defaultDisplaySection() {
return SettingsSection(
title: Text(translate("Display Settings")),
tiles: [
SettingsTile(
title: Text(translate('Display Settings')),
leading: Icon(Icons.desktop_windows_outlined),
trailing: Icon(Icons.arrow_forward_ios),
onPressed: (context) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return _DisplayPage();
}));
})
],
);
}
}
void showServerSettings(OverlayDialogManager dialogManager) async {
@@ -623,3 +641,181 @@ class ScanButton extends StatelessWidget {
);
}
}
class _DisplayPage extends StatefulWidget {
const _DisplayPage({super.key});
@override
State<_DisplayPage> createState() => __DisplayPageState();
}
class __DisplayPageState extends State<_DisplayPage> {
@override
Widget build(BuildContext context) {
final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings());
final h264 = codecsJson['h264'] ?? false;
final h265 = codecsJson['h265'] ?? false;
var codecList = [
_RadioEntry('Auto', 'auto'),
_RadioEntry('VP8', 'vp8'),
_RadioEntry('VP9', 'vp9'),
_RadioEntry('AV1', 'av1'),
if (h264) _RadioEntry('H264', 'h264'),
if (h265) _RadioEntry('H265', 'h265')
];
RxBool showCustomImageQuality = false.obs;
return Scaffold(
appBar: AppBar(
leading: IconButton(
onPressed: () => Navigator.pop(context),
icon: Icon(Icons.arrow_back_ios)),
title: Text(translate('Display Settings')),
centerTitle: true,
),
body: SettingsList(sections: [
SettingsSection(
tiles: [
_getPopupDialogRadioEntry(
title: 'Default View Style',
list: [
_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);
},
),
_getPopupDialogRadioEntry(
title: 'Default Image Quality',
list: [
_RadioEntry('Good image quality', kRemoteImageQualityBest),
_RadioEntry('Balanced', kRemoteImageQualityBalanced),
_RadioEntry('Optimize reaction time', kRemoteImageQualityLow),
_RadioEntry('Custom', kRemoteImageQualityCustom),
],
getter: () {
final v = bind.mainGetUserDefaultOption(key: 'image_quality');
showCustomImageQuality.value = v == kRemoteImageQualityCustom;
return v;
},
asyncSetter: (value) async {
await bind.mainSetUserDefaultOption(
key: 'image_quality', value: value);
showCustomImageQuality.value =
value == kRemoteImageQualityCustom;
},
tail: customImageQualitySetting(),
showTail: showCustomImageQuality,
notCloseValue: kRemoteImageQualityCustom,
),
_getPopupDialogRadioEntry(
title: 'Default Codec',
list: codecList,
getter: () =>
bind.mainGetUserDefaultOption(key: 'codec-preference'),
asyncSetter: (value) async {
await bind.mainSetUserDefaultOption(
key: 'codec-preference', value: value);
},
),
],
),
SettingsSection(
title: Text(translate('Other Default Options')),
tiles: [
otherRow('Show remote cursor', 'show_remote_cursor'),
otherRow('Show quality monitor', 'show_quality_monitor'),
otherRow('Mute', 'disable_audio'),
otherRow('Disable clipboard', 'disable_clipboard'),
otherRow('Lock after session end', 'lock_after_session_end'),
otherRow('Privacy mode', 'privacy_mode'),
otherRow('Touch mode', 'touch-mode'),
],
),
]),
);
}
otherRow(String label, String key) {
final value = bind.mainGetUserDefaultOption(key: key) == 'Y';
return SettingsTile.switchTile(
initialValue: value,
title: Text(translate(label)),
onToggle: (b) async {
await bind.mainSetUserDefaultOption(key: key, value: b ? 'Y' : '');
setState(() {});
},
);
}
}
class _RadioEntry {
final String label;
final String value;
_RadioEntry(this.label, this.value);
}
typedef _RadioEntryGetter = String Function();
typedef _RadioEntrySetter = Future<void> Function(String);
_getPopupDialogRadioEntry({
required String title,
required List<_RadioEntry> list,
required _RadioEntryGetter getter,
required _RadioEntrySetter asyncSetter,
Widget? tail,
RxBool? showTail,
String? notCloseValue,
}) {
RxString groupValue = ''.obs;
RxString valueText = ''.obs;
init() {
groupValue.value = getter();
final e = list.firstWhereOrNull((e) => e.value == groupValue.value);
if (e != null) {
valueText.value = e.label;
}
}
init();
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();
}
}
return CustomAlertDialog(
content: Obx(
() => Column(children: [
...list
.map((e) => getRadio(Text(translate(e.label)), e.value,
groupValue.value, (String? value) => onChanged(value)))
.toList(),
Offstage(
offstage:
!(tail != null && showTail != null && showTail.value == true),
child: tail,
),
]),
));
}, backDismiss: true, clickMaskDismiss: true);
}
return SettingsTile(
title: Text(translate(title)),
onPressed: (context) => showDialog(),
value: Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Obx(() => Text(translate(valueText.value))),
),
);
}

View File

@@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:get/get.dart';
import '../../common.dart';
@@ -147,59 +147,72 @@ void setTemporaryPasswordLengthDialog(
void showServerSettingsWithValue(
ServerConfig serverConfig, OverlayDialogManager dialogManager) async {
Map<String, dynamic> oldOptions = jsonDecode(await bind.mainGetOptions());
final oldCfg = ServerConfig.fromOptions(oldOptions);
var isInProgress = false;
final idCtrl = TextEditingController(text: serverConfig.idServer);
final relayCtrl = TextEditingController(text: serverConfig.relayServer);
final apiCtrl = TextEditingController(text: serverConfig.apiServer);
final keyCtrl = TextEditingController(text: serverConfig.key);
String? idServerMsg;
String? relayServerMsg;
String? apiServerMsg;
RxString idServerMsg = ''.obs;
RxString relayServerMsg = ''.obs;
RxString apiServerMsg = ''.obs;
final controllers = [idCtrl, relayCtrl, apiCtrl, keyCtrl];
final errMsgs = [
idServerMsg,
relayServerMsg,
apiServerMsg,
];
dialogManager.show((setState, close, context) {
Future<bool> validate() async {
if (idCtrl.text != oldCfg.idServer) {
final res = await validateAsync(idCtrl.text);
setState(() => idServerMsg = res);
if (idServerMsg != null) return false;
}
if (relayCtrl.text != oldCfg.relayServer) {
relayServerMsg = await validateAsync(relayCtrl.text);
if (relayServerMsg != null) return false;
}
if (apiCtrl.text != oldCfg.apiServer) {
if (apiServerMsg != null) return false;
}
return true;
Future<bool> submit() async {
setState(() {
isInProgress = true;
});
bool ret = await setServerConfig(
controllers,
errMsgs,
ServerConfig(
idServer: idCtrl.text.trim(),
relayServer: relayCtrl.text.trim(),
apiServer: apiCtrl.text.trim(),
key: keyCtrl.text.trim()));
setState(() {
isInProgress = false;
});
return ret;
}
return CustomAlertDialog(
title: Text(translate('ID/Relay Server')),
title: Row(
children: [
Expanded(child: Text(translate('ID/Relay Server'))),
...ServerConfigImportExportWidgets(controllers, errMsgs),
],
),
content: Form(
child: Column(
child: Obx(() => Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
controller: idCtrl,
decoration: InputDecoration(
labelText: translate('ID Server'),
errorText: idServerMsg),
errorText: idServerMsg.value.isEmpty
? null
: idServerMsg.value),
)
] +
[
TextFormField(
controller: relayCtrl,
decoration: InputDecoration(
labelText: translate('Relay Server'),
errorText: relayServerMsg.value.isEmpty
? null
: relayServerMsg.value),
)
] +
(isAndroid
? [
TextFormField(
controller: relayCtrl,
decoration: InputDecoration(
labelText: translate('Relay Server'),
errorText: relayServerMsg),
)
]
: []) +
[
TextFormField(
controller: apiCtrl,
@@ -214,7 +227,7 @@ void showServerSettingsWithValue(
return translate("invalid_http");
}
}
return apiServerMsg;
return null;
},
),
TextFormField(
@@ -225,7 +238,7 @@ void showServerSettingsWithValue(
),
// NOT use Offstage to wrap LinearProgressIndicator
if (isInProgress) const LinearProgressIndicator(),
])),
]))),
actions: [
dialogButton('Cancel', onPressed: () {
close();
@@ -233,35 +246,12 @@ void showServerSettingsWithValue(
dialogButton(
'OK',
onPressed: () async {
setState(() {
idServerMsg = null;
relayServerMsg = null;
apiServerMsg = null;
isInProgress = true;
});
if (await validate()) {
if (idCtrl.text != oldCfg.idServer) {
if (oldCfg.idServer.isNotEmpty) {
await gFFI.userModel.logOut();
}
bind.mainSetOption(
key: "custom-rendezvous-server", value: idCtrl.text);
}
if (relayCtrl.text != oldCfg.relayServer) {
bind.mainSetOption(key: "relay-server", value: relayCtrl.text);
}
if (keyCtrl.text != oldCfg.key) {
bind.mainSetOption(key: "key", value: keyCtrl.text);
}
if (apiCtrl.text != oldCfg.apiServer) {
bind.mainSetOption(key: "api-server", value: apiCtrl.text);
}
if (await submit()) {
close();
showToast(translate('Successful'));
} else {
showToast(translate('Failed'));
}
setState(() {
isInProgress = false;
});
},
),
],