Support core status check and force restart Optimize proxies page and access page Update flutter and pub dependencies Optimize more details
277 lines
7.5 KiB
Dart
277 lines
7.5 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'dart:isolate';
|
|
|
|
import 'package:fl_clash/common/common.dart';
|
|
import 'package:fl_clash/core/core.dart';
|
|
import 'package:fl_clash/core/interface.dart';
|
|
import 'package:fl_clash/enum/enum.dart';
|
|
import 'package:fl_clash/models/models.dart';
|
|
import 'package:fl_clash/state.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:path/path.dart';
|
|
|
|
class CoreController {
|
|
static CoreController? _instance;
|
|
late CoreHandlerInterface _interface;
|
|
|
|
CoreController._internal() {
|
|
if (system.isAndroid) {
|
|
_interface = coreLib!;
|
|
} else {
|
|
_interface = coreService!;
|
|
}
|
|
}
|
|
|
|
factory CoreController() {
|
|
_instance ??= CoreController._internal();
|
|
return _instance!;
|
|
}
|
|
|
|
Future<String> preload() {
|
|
return _interface.preload();
|
|
}
|
|
|
|
static Future<void> 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 = [MMDB, GEOIP, GEOSITE, ASN];
|
|
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<int> bytes = data.buffer.asUint8List();
|
|
await geoFile.writeAsBytes(bytes, flush: true);
|
|
}
|
|
} catch (e) {
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
Future<bool> init(int version) async {
|
|
await initGeo();
|
|
final homeDirPath = await appPath.homeDirPath;
|
|
return await _interface.init(
|
|
InitParams(homeDir: homeDirPath, version: version),
|
|
);
|
|
}
|
|
|
|
Future<void> shutdown() async {
|
|
await _interface.shutdown();
|
|
}
|
|
|
|
FutureOr<bool> get isInit => _interface.isInit;
|
|
|
|
FutureOr<String> validateConfig(String data) {
|
|
return _interface.validateConfig(data);
|
|
}
|
|
|
|
Future<String> updateConfig(UpdateParams updateParams) async {
|
|
return await _interface.updateConfig(updateParams);
|
|
}
|
|
|
|
Future<String> setupConfig(ClashConfig clashConfig) async {
|
|
await globalState.genConfigFile(clashConfig);
|
|
final params = await globalState.getSetupParams();
|
|
return await _interface.setupConfig(params);
|
|
}
|
|
|
|
Future<List<Group>> getProxiesGroups({
|
|
required ProxiesSortType sortType,
|
|
required DelayMap delayMap,
|
|
required SelectedMap selectedMap,
|
|
required String defaultTestUrl,
|
|
}) async {
|
|
final proxies = await _interface.getProxies();
|
|
return Isolate.run<List<Group>>(() {
|
|
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();
|
|
final groups = groupsRaw.map((e) => Group.fromJson(e)).toList();
|
|
return computeSort(
|
|
groups: groups,
|
|
sortType: sortType,
|
|
delayMap: delayMap,
|
|
selectedMap: selectedMap,
|
|
defaultTestUrl: defaultTestUrl,
|
|
);
|
|
});
|
|
}
|
|
|
|
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) async {
|
|
return await _interface.changeProxy(changeProxyParams);
|
|
}
|
|
|
|
Future<List<TrackerInfo>> getConnections() async {
|
|
final res = await _interface.getConnections();
|
|
final connectionsData = json.decode(res) as Map;
|
|
final connectionsRaw = connectionsData['connections'] as List? ?? [];
|
|
return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList();
|
|
}
|
|
|
|
void closeConnection(String id) {
|
|
_interface.closeConnection(id);
|
|
}
|
|
|
|
void closeConnections() {
|
|
_interface.closeConnections();
|
|
}
|
|
|
|
void resetConnections() {
|
|
_interface.resetConnections();
|
|
}
|
|
|
|
Future<List<ExternalProvider>> getExternalProviders() async {
|
|
final externalProvidersRawString = await _interface.getExternalProviders();
|
|
if (externalProvidersRawString.isEmpty) {
|
|
return [];
|
|
}
|
|
return Isolate.run<List<ExternalProvider>>(() {
|
|
final externalProviders =
|
|
(json.decode(externalProvidersRawString) as List<dynamic>)
|
|
.map((item) => ExternalProvider.fromJson(item))
|
|
.toList();
|
|
return externalProviders;
|
|
});
|
|
}
|
|
|
|
Future<ExternalProvider?> getExternalProvider(
|
|
String externalProviderName,
|
|
) async {
|
|
final externalProvidersRawString = await _interface.getExternalProvider(
|
|
externalProviderName,
|
|
);
|
|
if (externalProvidersRawString.isEmpty) {
|
|
return null;
|
|
}
|
|
return ExternalProvider.fromJson(json.decode(externalProvidersRawString));
|
|
}
|
|
|
|
Future<String> updateGeoData(UpdateGeoDataParams params) {
|
|
return _interface.updateGeoData(params);
|
|
}
|
|
|
|
Future<String> sideLoadExternalProvider({
|
|
required String providerName,
|
|
required String data,
|
|
}) {
|
|
return _interface.sideLoadExternalProvider(
|
|
providerName: providerName,
|
|
data: data,
|
|
);
|
|
}
|
|
|
|
Future<String> updateExternalProvider({required String providerName}) async {
|
|
return _interface.updateExternalProvider(providerName);
|
|
}
|
|
|
|
Future<bool> startListener() async {
|
|
return await _interface.startListener();
|
|
}
|
|
|
|
Future<bool> stopListener() async {
|
|
return await _interface.stopListener();
|
|
}
|
|
|
|
Future<Delay> getDelay(String url, String proxyName) async {
|
|
final data = await _interface.asyncTestDelay(url, proxyName);
|
|
return Delay.fromJson(json.decode(data));
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getConfig(String id) async {
|
|
final profilePath = await appPath.getProfilePath(id);
|
|
final res = await _interface.getConfig(profilePath);
|
|
if (res.isSuccess) {
|
|
return res.data;
|
|
} else {
|
|
throw res.message;
|
|
}
|
|
}
|
|
|
|
Future<Traffic> getTraffic(bool onlyStatisticsProxy) async {
|
|
final trafficString = await _interface.getTraffic(onlyStatisticsProxy);
|
|
if (trafficString.isEmpty) {
|
|
return Traffic();
|
|
}
|
|
return Traffic.fromJson(json.decode(trafficString));
|
|
}
|
|
|
|
Future<IpInfo?> getCountryCode(String ip) async {
|
|
final countryCode = await _interface.getCountryCode(ip);
|
|
if (countryCode.isEmpty) {
|
|
return null;
|
|
}
|
|
return IpInfo(ip: ip, countryCode: countryCode);
|
|
}
|
|
|
|
Future<Traffic> getTotalTraffic(bool onlyStatisticsProxy) async {
|
|
final totalTrafficString = await _interface.getTotalTraffic(
|
|
onlyStatisticsProxy,
|
|
);
|
|
if (totalTrafficString.isEmpty) {
|
|
return Traffic();
|
|
}
|
|
return Traffic.fromJson(json.decode(totalTrafficString));
|
|
}
|
|
|
|
Future<int> getMemory() async {
|
|
final value = await _interface.getMemory();
|
|
if (value.isEmpty) {
|
|
return 0;
|
|
}
|
|
return int.parse(value);
|
|
}
|
|
|
|
void resetTraffic() {
|
|
_interface.resetTraffic();
|
|
}
|
|
|
|
void startLog() {
|
|
_interface.startLog();
|
|
}
|
|
|
|
void stopLog() {
|
|
_interface.stopLog();
|
|
}
|
|
|
|
Future<void> requestGc() async {
|
|
await _interface.forceGc();
|
|
}
|
|
|
|
Future<void> destroy() async {
|
|
await _interface.destroy();
|
|
}
|
|
|
|
Future<void> crash() async {
|
|
await _interface.crash();
|
|
}
|
|
|
|
Future<String> deleteFile(String path) async {
|
|
return await _interface.deleteFile(path);
|
|
}
|
|
}
|
|
|
|
final coreController = CoreController();
|