import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:isolate'; import 'package:fl_clash/clash/clash.dart'; import 'package:fl_clash/clash/interface.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; import 'package:flutter/services.dart'; import 'package:path/path.dart'; class ClashCore { static ClashCore? _instance; late ClashHandlerInterface clashInterface; ClashCore._internal() { if (Platform.isAndroid) { clashInterface = clashLib!; } else { clashInterface = clashService!; } } factory ClashCore() { _instance ??= ClashCore._internal(); return _instance!; } Future preload() { return clashInterface.preload(); } static Future initGeo() async { final homePath = await appPath.homeDirPath; final homeDir = Directory(homePath); final isExists = await homeDir.exists(); if (!isExists) { await homeDir.create(recursive: true); } const geoFileNameList = [ mmdbFileName, geoIpFileName, geoSiteFileName, asnFileName, ]; try { for (final geoFileName in geoFileNameList) { final geoFile = File( join(homePath, geoFileName), ); final isExists = await geoFile.exists(); if (isExists) { continue; } final data = await rootBundle.load('assets/data/$geoFileName'); List bytes = data.buffer.asUint8List(); await geoFile.writeAsBytes(bytes, flush: true); } } catch (e) { exit(0); } } Future init() async { await initGeo(); final homeDirPath = await appPath.homeDirPath; return await clashInterface.init(homeDirPath); } Future setState(CoreState state) async { return await clashInterface.setState(state); } shutdown() async { await clashInterface.shutdown(); } FutureOr get isInit => clashInterface.isInit; FutureOr validateConfig(String data) { return clashInterface.validateConfig(data); } Future updateConfig(UpdateConfigParams updateConfigParams) async { return await clashInterface.updateConfig(updateConfigParams); } Future> getProxiesGroups() async { final proxiesRawString = await clashInterface.getProxies(); return Isolate.run>(() { if (proxiesRawString.isEmpty) return []; final proxies = (json.decode(proxiesRawString) ?? {}) as Map; if (proxies.isEmpty) return []; final groupNames = [ UsedProxy.GLOBAL.name, ...(proxies[UsedProxy.GLOBAL.name]["all"] as List).where((e) { final proxy = proxies[e] ?? {}; return GroupTypeExtension.valueList.contains(proxy['type']); }) ]; final groupsRaw = groupNames.map((groupName) { final group = proxies[groupName]; group["all"] = ((group["all"] ?? []) as List) .map( (name) => proxies[name], ) .where((proxy) => proxy != null) .toList(); return group; }).toList(); return groupsRaw .map( (e) => Group.fromJson(e), ) .toList(); }); } FutureOr changeProxy(ChangeProxyParams changeProxyParams) async { return await clashInterface.changeProxy(changeProxyParams); } Future> getConnections() async { final res = await clashInterface.getConnections(); final connectionsData = json.decode(res) as Map; final connectionsRaw = connectionsData['connections'] as List? ?? []; return connectionsRaw.map((e) => Connection.fromJson(e)).toList(); } closeConnection(String id) { clashInterface.closeConnection(id); } closeConnections() { clashInterface.closeConnections(); } Future> getExternalProviders() async { final externalProvidersRawString = await clashInterface.getExternalProviders(); if (externalProvidersRawString.isEmpty) { return []; } return Isolate.run>( () { final externalProviders = (json.decode(externalProvidersRawString) as List) .map( (item) => ExternalProvider.fromJson(item), ) .toList(); return externalProviders; }, ); } Future getExternalProvider( String externalProviderName) async { final externalProvidersRawString = await clashInterface.getExternalProvider(externalProviderName); if (externalProvidersRawString.isEmpty) { return null; } if (externalProvidersRawString.isEmpty) { return null; } return ExternalProvider.fromJson(json.decode(externalProvidersRawString)); } Future updateGeoData(UpdateGeoDataParams params) { return clashInterface.updateGeoData(params); } Future sideLoadExternalProvider({ required String providerName, required String data, }) { return clashInterface.sideLoadExternalProvider( providerName: providerName, data: data); } Future updateExternalProvider({ required String providerName, }) async { return clashInterface.updateExternalProvider(providerName); } startListener() async { await clashInterface.startListener(); } stopListener() async { await clashInterface.stopListener(); } Future getDelay(String url, String proxyName) async { final data = await clashInterface.asyncTestDelay(url, proxyName); return Delay.fromJson(json.decode(data)); } Future getTraffic() async { final trafficString = await clashInterface.getTraffic(); if (trafficString.isEmpty) { return Traffic(); } return Traffic.fromMap(json.decode(trafficString)); } Future getCountryCode(String ip) async { final countryCode = await clashInterface.getCountryCode(ip); if (countryCode.isEmpty) { return null; } return IpInfo( ip: ip, countryCode: countryCode, ); } Future getTotalTraffic() async { final totalTrafficString = await clashInterface.getTotalTraffic(); if (totalTrafficString.isEmpty) { return Traffic(); } return Traffic.fromMap(json.decode(totalTrafficString)); } Future getMemory() async { final value = await clashInterface.getMemory(); if (value.isEmpty) { return 0; } return int.parse(value); } Future getProfile(String id) async { final res = await clashInterface.getProfile(id); if (res.isEmpty) { return null; } return ClashConfig.fromJson(json.decode(res)); } resetTraffic() { clashInterface.resetTraffic(); } startLog() { clashInterface.startLog(); } stopLog() { clashInterface.stopLog(); } requestGc() { clashInterface.forceGc(); } destroy() async { await clashInterface.destroy(); } } final clashCore = ClashCore();