285 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
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/consts.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,
 | 
						|
    required bool showMoreQuality}) {
 | 
						|
  if (initQuality < kMinQuality ||
 | 
						|
      initQuality > (showMoreQuality ? kMaxMoreQuality : kMaxQuality)) {
 | 
						|
    initQuality = kDefaultQuality;
 | 
						|
  }
 | 
						|
  if (initFps < kMinFps || initFps > kMaxFps) {
 | 
						|
    initFps = kDefaultFps;
 | 
						|
  }
 | 
						|
  final qualityValue = initQuality.obs;
 | 
						|
  final fpsValue = initFps.obs;
 | 
						|
 | 
						|
  final RxBool moreQualityChecked = RxBool(qualityValue.value > kMaxQuality);
 | 
						|
  final debouncerQuality = Debouncer<double>(
 | 
						|
    Duration(milliseconds: 1000),
 | 
						|
    onChanged: (double v) {
 | 
						|
      setQuality(v);
 | 
						|
    },
 | 
						|
    initialValue: qualityValue.value,
 | 
						|
  );
 | 
						|
  final debouncerFps = Debouncer<double>(
 | 
						|
    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: kMinQuality,
 | 
						|
                  max: moreQualityChecked.value ? kMaxMoreQuality : kMaxQuality,
 | 
						|
                  divisions: moreQualityChecked.value
 | 
						|
                      ? ((kMaxMoreQuality - kMinQuality) / 10).round()
 | 
						|
                      : ((kMaxQuality - kMinQuality) / 5).round(),
 | 
						|
                  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 (showMoreQuality && !isMobile)
 | 
						|
                Expanded(
 | 
						|
                    flex: 1,
 | 
						|
                    child: Row(
 | 
						|
                      children: [
 | 
						|
                        Checkbox(
 | 
						|
                          value: moreQualityChecked.value,
 | 
						|
                          onChanged: onMoreChanged,
 | 
						|
                        ),
 | 
						|
                        Expanded(
 | 
						|
                          child: Text(translate('More')),
 | 
						|
                        )
 | 
						|
                      ],
 | 
						|
                    ))
 | 
						|
            ],
 | 
						|
          )),
 | 
						|
      if (showMoreQuality && 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: kMinFps,
 | 
						|
                    max: kMaxFps,
 | 
						|
                    divisions: ((kMaxFps - kMinFps) / 5).round(),
 | 
						|
                    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)) ??
 | 
						|
          kDefaultQuality);
 | 
						|
  var initFps = (double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ??
 | 
						|
      kDefaultFps);
 | 
						|
 | 
						|
  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,
 | 
						|
      showMoreQuality: true);
 | 
						|
}
 | 
						|
 | 
						|
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(
 | 
						|
      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))
 | 
						|
  ];
 | 
						|
}
 |