import 'dart:io'; import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; import 'package:naiyouwl/app/bean/proxie.dart'; import 'package:naiyouwl/app/controller/controllers.dart'; import 'package:naiyouwl/app/data/model/NodeMode.dart'; import 'package:naiyouwl/app/network/api_service.dart'; import 'package:naiyouwl/app/utils/logger.dart'; import 'package:naiyouwl/app/utils/system_proxy.dart'; import 'package:naiyouwl/app/utils/utils.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; class GlobalController extends GetxController { late BuildContext context; var nodeModes = [].obs; var isLoading = false.obs; var errorMsg = ''.obs; var systemProxySwitchIng = false.obs; // 策略组 var proxieGroups = [].obs; // 代理集 var proxieProviders = [].obs; // 代理 var proxieProxies = [].obs; // 所有节点 var allProxies = {}.obs; final List groupInternalTypes = ['DIRECT', 'REJECT', 'GLOBAL']; final List groupTypes = [ ProxieProxieType.selector, ProxieProxieType.urltest, ProxieProxieType.fallback, ProxieProxieType.loadbalance, ]; Future init(BuildContext context) async { this.context = context; watchExit(); // init plugins await controllers.tray.initTray(); controllers.window.initWindow(); //controllers.protocol.initProtocol(); // init config await controllers.config.initConfig(); final language = controllers.config.config.value.language.split('_'); //await controllers.pageSetting.applyLanguage(Locale(language[0], language[1])); // init service await controllers.service.startService(); if (controllers.service.serviceStatus.value != RunningState.running) return; // init clash core await controllers.service.startClashCore(); if (controllers.service.coreStatus.value != RunningState.running) return; await controllers.core.updateVersion(); // await controllers.pageProxie.updateDate(); initRegularlyUpdate(); } Future fetchNodes() async { nodeModes.value = await ApiService().getNode("/api/client/v4/nodes?vless=1"); } Future systemProxySwitch(bool open) async { systemProxySwitchIng.value = true; await SystemProxy.instance.set(open ? controllers.core.proxyConfig : SystemProxyConfig()); await controllers.config.setSystemProxy(open); systemProxySwitchIng.value = false; } Future _updateProxie() async { final proxie = await controllers.core.fetchProxie(); final global = proxie.proxies["GLOBAL"]!; proxieGroups.value = global.all! .where((it) => !groupInternalTypes.contains(it) && groupTypes.contains(proxie.proxies[it]!.type)) .map((it) => proxie.proxies[it]!) .toList(); proxieProxies.value = global.all! .where((it) => !groupInternalTypes.contains(it) && !groupTypes.contains(proxie.proxies[it]!.type)) .map((it) => proxie.proxies[it]!) .toList(); if (controllers.core.config.value.mode == 'global') proxieGroups.insert(0, global); } Future _updateProxieProvider() async { proxieProviders.value = (await controllers.core.fetchProxieProvider()).providers.values.where((it) => it.vehicleType != 'Compatible').toList(); for (final it in proxieProviders) { it.proxies.sort((a, b) { if (a.delay == 0) return 1; if (b.delay == 0) return -1; return a.delay - b.delay; }); } } Future updateDate() async { log.debug('controller.proxie.updateDate()'); await controllers.core.updateConfig(); await _updateProxie(); await _updateProxieProvider(); allProxies.clear(); for (final provide in proxieProviders) { for (final it in provide.proxies) { allProxies[it.name] = it; } } for (final it in proxieProxies) { allProxies[it.name] = it; } for (final it in proxieGroups) { allProxies[it.name] = it; } proxieGroups.refresh(); proxieProxies.refresh(); proxieProviders.refresh(); allProxies.refresh(); } Future handleSetProxieGroup(ProxieProxiesItem proxie, String value) async { if (proxie.now == value) return; await controllers.core.fetchSetProxieGroup(proxie.name, value); await updateDate(); if (controllers.config.config.value.breakConnections) { final conn = await controllers.core.fetchConnection(); for (final it in conn.connections) { if (it.chains.contains(proxie.name)) controllers.core.fetchCloseConnections(it.id); } } } void watchExit() { // watch process kill // ref https://github.com/dart-lang/sdk/issues/12170 if (Platform.isMacOS) { // windows not support https://github.com/dart-lang/sdk/issues/28603 // for macos 任务管理器退出进程 ProcessSignal.sigterm.watch().listen((_) { stdout.writeln('exit: sigterm'); handleExit(); }); } // for macos, windows ctrl+c ProcessSignal.sigint.watch().listen((_) { stdout.writeln('exit: sigint'); handleExit(); }); } void initRegularlyUpdate() { Future.delayed(const Duration(minutes: 5)).then((_) async { for (final it in controllers.config.config.value.subs) { try { if (it.url == null || it.url!.isEmpty) continue; if (((DateTime.now().millisecondsSinceEpoch ~/ 1000) - (it.updateTime ?? 0)) < controllers.config.config.value.updateInterval) continue; final chenged = await controllers.config.updateSub(it); if (!chenged) continue; if (it.name != controllers.config.config.value.selected) continue; // restart clash core await controllers.service.reloadClashCore(); await Future.delayed(const Duration(seconds: 20)); } catch (_) {} } initRegularlyUpdate(); }); } Future handleExit() async { await controllers.service.stopService(); await trayManager.destroy(); await windowManager.destroy(); // exit(0); } @override void dispose() { controllers.tray.dispose(); controllers.window.dispose(); //controllers.protocol.dispose(); super.dispose(); } }