2023-08-31 20:30:20 +08:00
|
|
|
import 'package:debounce_throttle/debounce_throttle.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:flutter_hbb/common.dart';
|
|
|
|
import 'package:flutter_hbb/models/platform_model.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
|
|
|
customImageQualityWidget(
|
|
|
|
{required double initQuality,
|
|
|
|
required Function(double) setQuality,
|
2023-11-02 20:39:56 +08:00
|
|
|
required bool showMoreQuality}) {
|
2023-11-09 10:20:31 +08:00
|
|
|
final maxValue = showMoreQuality ? 2000 : 10;
|
|
|
|
if (initQuality < 10 || initQuality > maxValue) {
|
2023-11-02 20:39:56 +08:00
|
|
|
initQuality = 50;
|
|
|
|
}
|
2023-08-31 20:30:20 +08:00
|
|
|
final qualityValue = initQuality.obs;
|
|
|
|
|
|
|
|
final RxBool moreQualityChecked = RxBool(qualityValue.value > 100);
|
|
|
|
final debouncerQuality = Debouncer<double>(
|
|
|
|
Duration(milliseconds: 1000),
|
|
|
|
onChanged: (double v) {
|
|
|
|
setQuality(v);
|
|
|
|
},
|
|
|
|
initialValue: qualityValue.value,
|
|
|
|
);
|
|
|
|
|
|
|
|
onMoreChanged(bool? value) {
|
|
|
|
if (value == null) return;
|
|
|
|
moreQualityChecked.value = value;
|
|
|
|
if (!value && qualityValue.value > 100) {
|
|
|
|
qualityValue.value = 100;
|
|
|
|
}
|
|
|
|
debouncerQuality.value = qualityValue.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Column(
|
|
|
|
children: [
|
|
|
|
Obx(() => Row(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
flex: 3,
|
|
|
|
child: Slider(
|
|
|
|
value: qualityValue.value,
|
|
|
|
min: 10.0,
|
|
|
|
max: moreQualityChecked.value ? 2000 : 100,
|
|
|
|
divisions: moreQualityChecked.value ? 199 : 18,
|
|
|
|
onChanged: (double value) async {
|
|
|
|
qualityValue.value = value;
|
|
|
|
debouncerQuality.value = value;
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
flex: 1,
|
|
|
|
child: Text(
|
|
|
|
'${qualityValue.value.round()}%',
|
|
|
|
style: const TextStyle(fontSize: 15),
|
|
|
|
)),
|
|
|
|
Expanded(
|
|
|
|
flex: isMobile ? 2 : 1,
|
|
|
|
child: Text(
|
|
|
|
translate('Bitrate'),
|
|
|
|
style: const TextStyle(fontSize: 15),
|
|
|
|
)),
|
|
|
|
// mobile doesn't have enough space
|
2023-11-02 20:39:56 +08:00
|
|
|
if (showMoreQuality && !isMobile)
|
2023-08-31 20:30:20 +08:00
|
|
|
Expanded(
|
|
|
|
flex: 1,
|
|
|
|
child: Row(
|
|
|
|
children: [
|
|
|
|
Checkbox(
|
|
|
|
value: moreQualityChecked.value,
|
|
|
|
onChanged: onMoreChanged,
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Text(translate('More')),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
))
|
|
|
|
],
|
|
|
|
)),
|
2023-11-02 20:39:56 +08:00
|
|
|
if (showMoreQuality && isMobile)
|
2023-08-31 20:30:20 +08:00
|
|
|
Obx(() => Row(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: Align(
|
|
|
|
alignment: Alignment.centerRight,
|
|
|
|
child: Checkbox(
|
|
|
|
value: moreQualityChecked.value,
|
|
|
|
onChanged: onMoreChanged,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Text(translate('More')),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
)),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
customImageQualitySetting() {
|
|
|
|
final qualityKey = 'custom_image_quality';
|
|
|
|
|
|
|
|
var initQuality =
|
|
|
|
(double.tryParse(bind.mainGetUserDefaultOption(key: qualityKey)) ?? 50.0);
|
|
|
|
|
|
|
|
return customImageQualityWidget(
|
|
|
|
initQuality: initQuality,
|
|
|
|
setQuality: (v) {
|
|
|
|
bind.mainSetUserDefaultOption(key: qualityKey, value: v.toString());
|
|
|
|
},
|
2023-11-02 20:39:56 +08:00
|
|
|
showMoreQuality: true);
|
2023-08-31 20:30:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> setServerConfig(
|
|
|
|
List<TextEditingController> controllers,
|
|
|
|
List<RxString> errMsgs,
|
|
|
|
ServerConfig config,
|
|
|
|
) async {
|
|
|
|
config.idServer = config.idServer.trim();
|
|
|
|
config.relayServer = config.relayServer.trim();
|
|
|
|
config.apiServer = config.apiServer.trim();
|
|
|
|
config.key = config.key.trim();
|
|
|
|
// id
|
|
|
|
if (config.idServer.isNotEmpty) {
|
|
|
|
errMsgs[0].value =
|
|
|
|
translate(await bind.mainTestIfValidServer(server: config.idServer));
|
|
|
|
if (errMsgs[0].isNotEmpty) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// relay
|
|
|
|
if (config.relayServer.isNotEmpty) {
|
|
|
|
errMsgs[1].value =
|
|
|
|
translate(await bind.mainTestIfValidServer(server: config.relayServer));
|
|
|
|
if (errMsgs[1].isNotEmpty) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// api
|
|
|
|
if (config.apiServer.isNotEmpty) {
|
|
|
|
if (!config.apiServer.startsWith('http://') &&
|
|
|
|
!config.apiServer.startsWith('https://')) {
|
|
|
|
errMsgs[2].value =
|
|
|
|
'${translate("API Server")}: ${translate("invalid_http")}';
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
final oldApiServer = await bind.mainGetApiServer();
|
|
|
|
|
|
|
|
// should set one by one
|
|
|
|
await bind.mainSetOption(
|
|
|
|
key: 'custom-rendezvous-server', value: config.idServer);
|
|
|
|
await bind.mainSetOption(key: 'relay-server', value: config.relayServer);
|
|
|
|
await bind.mainSetOption(key: 'api-server', value: config.apiServer);
|
|
|
|
await bind.mainSetOption(key: 'key', value: config.key);
|
|
|
|
|
|
|
|
final newApiServer = await bind.mainGetApiServer();
|
|
|
|
if (oldApiServer.isNotEmpty &&
|
|
|
|
oldApiServer != newApiServer &&
|
|
|
|
gFFI.userModel.isLogin) {
|
|
|
|
gFFI.userModel.logOut(apiServer: oldApiServer);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<Widget> ServerConfigImportExportWidgets(
|
|
|
|
List<TextEditingController> controllers,
|
|
|
|
List<RxString> errMsgs,
|
|
|
|
) {
|
|
|
|
import() {
|
|
|
|
Clipboard.getData(Clipboard.kTextPlain).then((value) {
|
|
|
|
final text = value?.text;
|
|
|
|
if (text != null && text.isNotEmpty) {
|
|
|
|
try {
|
|
|
|
final sc = ServerConfig.decode(text);
|
|
|
|
if (sc.idServer.isNotEmpty) {
|
|
|
|
controllers[0].text = sc.idServer;
|
|
|
|
controllers[1].text = sc.relayServer;
|
|
|
|
controllers[2].text = sc.apiServer;
|
|
|
|
controllers[3].text = sc.key;
|
|
|
|
Future<bool> success = setServerConfig(controllers, errMsgs, sc);
|
|
|
|
success.then((value) {
|
|
|
|
if (value) {
|
|
|
|
showToast(
|
|
|
|
translate('Import server configuration successfully'));
|
|
|
|
} else {
|
|
|
|
showToast(translate('Invalid server configuration'));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
showToast(translate('Invalid server configuration'));
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
showToast(translate('Invalid server configuration'));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
showToast(translate('Clipboard is empty'));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export() {
|
|
|
|
final text = ServerConfig(
|
|
|
|
idServer: controllers[0].text.trim(),
|
|
|
|
relayServer: controllers[1].text.trim(),
|
|
|
|
apiServer: controllers[2].text.trim(),
|
|
|
|
key: controllers[3].text.trim())
|
|
|
|
.encode();
|
|
|
|
debugPrint("ServerConfig export: $text");
|
|
|
|
Clipboard.setData(ClipboardData(text: text));
|
|
|
|
showToast(translate('Export server configuration successfully'));
|
|
|
|
}
|
|
|
|
|
|
|
|
return [
|
|
|
|
Tooltip(
|
2023-11-06 20:12:01 +08:00
|
|
|
message: translate('Import server config'),
|
2023-08-31 20:30:20 +08:00
|
|
|
child: IconButton(
|
|
|
|
icon: Icon(Icons.paste, color: Colors.grey), onPressed: import),
|
|
|
|
),
|
|
|
|
Tooltip(
|
|
|
|
message: translate('Export Server Config'),
|
|
|
|
child: IconButton(
|
|
|
|
icon: Icon(Icons.copy, color: Colors.grey), onPressed: export))
|
|
|
|
];
|
|
|
|
}
|