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 double initFps, required Function(double) setQuality, required Function(double) setFps, required bool showFps}) { final qualityValue = initQuality.obs; final fpsValue = initFps.obs; final RxBool moreQualityChecked = RxBool(qualityValue.value > 100); final debouncerQuality = Debouncer( Duration(milliseconds: 1000), onChanged: (double v) { setQuality(v); }, initialValue: qualityValue.value, ); final debouncerFps = Debouncer( Duration(milliseconds: 1000), onChanged: (double v) { setFps(v); }, initialValue: fpsValue.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 if (!isMobile) Expanded( flex: 1, child: Row( children: [ Checkbox( value: moreQualityChecked.value, onChanged: onMoreChanged, ), Expanded( child: Text(translate('More')), ) ], )) ], )), if (isMobile) Obx(() => Row( children: [ Expanded( child: Align( alignment: Alignment.centerRight, child: Checkbox( value: moreQualityChecked.value, onChanged: onMoreChanged, ), ), ), Expanded( child: Text(translate('More')), ) ], )), if (showFps) Obx(() => Row( children: [ Expanded( flex: 3, child: Slider( value: fpsValue.value, min: 5.0, max: 120.0, divisions: 23, onChanged: (double value) async { fpsValue.value = value; debouncerFps.value = value; }, ), ), Expanded( flex: 1, child: Text( '${fpsValue.value.round()}', style: const TextStyle(fontSize: 15), )), Expanded( flex: 2, child: Text( translate('FPS'), style: const TextStyle(fontSize: 15), )) ], )), ], ); } customImageQualitySetting() { final qualityKey = 'custom_image_quality'; final fpsKey = 'custom-fps'; var initQuality = (double.tryParse(bind.mainGetUserDefaultOption(key: qualityKey)) ?? 50.0); if (initQuality < 10 || initQuality > 2000) { initQuality = 50; } var initFps = (double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ?? 30.0); if (initFps < 5 || initFps > 120) { initFps = 30; } return customImageQualityWidget( initQuality: initQuality, initFps: initFps, setQuality: (v) { bind.mainSetUserDefaultOption(key: qualityKey, value: v.toString()); }, setFps: (v) { bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString()); }, showFps: true); } Future setServerConfig( List controllers, List 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 ServerConfigImportExportWidgets( List controllers, List 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 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( message: translate('Import Server Config'), 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)) ]; }