import 'dart:async'; import 'dart:convert'; import 'package:fl_clash/clash/message.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; mixin ClashInterface { Future init(InitParams params); Future preload(); Future shutdown(); Future get isInit; Future forceGc(); FutureOr validateConfig(String data); Future asyncTestDelay(String url, String proxyName); FutureOr updateConfig(UpdateConfigParams updateConfigParams); FutureOr getProxies(); FutureOr changeProxy(ChangeProxyParams changeProxyParams); Future startListener(); Future stopListener(); FutureOr getExternalProviders(); FutureOr? getExternalProvider(String externalProviderName); Future updateGeoData(UpdateGeoDataParams params); Future sideLoadExternalProvider({ required String providerName, required String data, }); Future updateExternalProvider(String providerName); FutureOr getTraffic(); FutureOr getTotalTraffic(); FutureOr getCountryCode(String ip); FutureOr getMemory(); resetTraffic(); startLog(); stopLog(); FutureOr getConnections(); FutureOr closeConnection(String id); FutureOr closeConnections(); FutureOr getProfile(String id); Future setState(CoreState state); } mixin AndroidClashInterface { Future setFdMap(int fd); Future setProcessMap(ProcessMapItem item); // Future stopTun(); Future updateDns(String value); Future getAndroidVpnOptions(); Future getCurrentProfileName(); Future getRunTime(); } abstract class ClashHandlerInterface with ClashInterface { Map callbackCompleterMap = {}; Future nextHandleResult(ActionResult result, Completer? completer) => Future.value(false); handleResult(ActionResult result) async { final completer = callbackCompleterMap[result.id]; try { switch (result.method) { case ActionMethod.initClash: case ActionMethod.shutdown: case ActionMethod.getIsInit: case ActionMethod.startListener: case ActionMethod.resetTraffic: case ActionMethod.closeConnections: case ActionMethod.closeConnection: case ActionMethod.stopListener: case ActionMethod.setState: completer?.complete(result.data as bool); return; case ActionMethod.changeProxy: case ActionMethod.getProxies: case ActionMethod.getTraffic: case ActionMethod.getTotalTraffic: case ActionMethod.asyncTestDelay: case ActionMethod.getConnections: case ActionMethod.getExternalProviders: case ActionMethod.getExternalProvider: case ActionMethod.validateConfig: case ActionMethod.updateConfig: case ActionMethod.updateGeoData: case ActionMethod.updateExternalProvider: case ActionMethod.sideLoadExternalProvider: case ActionMethod.getCountryCode: case ActionMethod.getMemory: completer?.complete(result.data as String); return; case ActionMethod.message: clashMessage.controller.add(result.data as String); completer?.complete(true); return; default: final isHandled = await nextHandleResult(result, completer); if (isHandled) { return; } completer?.complete(result.data); } } catch (_) { commonPrint.log(result.id); } } sendMessage(String message); reStart(); FutureOr destroy(); Future invoke({ required ActionMethod method, dynamic data, Duration? timeout, FutureOr Function()? onTimeout, }) async { final id = "${method.name}#${utils.id}"; callbackCompleterMap[id] = Completer(); dynamic defaultValue; if (T == String) { defaultValue = ""; } if (T == bool) { defaultValue = false; } sendMessage( json.encode( Action( id: id, method: method, data: data, defaultValue: defaultValue, ), ), ); return (callbackCompleterMap[id] as Completer).safeFuture( timeout: timeout, onLast: () { callbackCompleterMap.remove(id); }, onTimeout: onTimeout ?? () { return defaultValue; }, functionName: id, ); } @override Future init(InitParams params) { return invoke( method: ActionMethod.initClash, data: json.encode(params), ); } @override Future setState(CoreState state) { return invoke( method: ActionMethod.setState, data: json.encode(state), ); } @override shutdown() async { return await invoke( method: ActionMethod.shutdown, ); } @override Future get isInit { return invoke( method: ActionMethod.getIsInit, ); } @override Future forceGc() { return invoke( method: ActionMethod.forceGc, ); } @override FutureOr validateConfig(String data) { return invoke( method: ActionMethod.validateConfig, data: data, ); } @override Future updateConfig(UpdateConfigParams updateConfigParams) async { return await invoke( method: ActionMethod.updateConfig, data: json.encode(updateConfigParams), timeout: Duration(minutes: 2), ); } @override Future getProxies() { return invoke( method: ActionMethod.getProxies, timeout: Duration(seconds: 5), ); } @override FutureOr changeProxy(ChangeProxyParams changeProxyParams) { return invoke( method: ActionMethod.changeProxy, data: json.encode(changeProxyParams), ); } @override FutureOr getExternalProviders() { return invoke( method: ActionMethod.getExternalProviders, ); } @override FutureOr getExternalProvider(String externalProviderName) { return invoke( method: ActionMethod.getExternalProvider, data: externalProviderName, ); } @override Future updateGeoData(UpdateGeoDataParams params) { return invoke( method: ActionMethod.updateGeoData, data: json.encode(params), timeout: Duration(minutes: 1)); } @override Future sideLoadExternalProvider({ required String providerName, required String data, }) { return invoke( method: ActionMethod.sideLoadExternalProvider, data: json.encode({ "providerName": providerName, "data": data, }), ); } @override Future updateExternalProvider(String providerName) { return invoke( method: ActionMethod.updateExternalProvider, data: providerName, timeout: Duration(minutes: 1), ); } @override FutureOr getConnections() { return invoke( method: ActionMethod.getConnections, ); } @override Future closeConnections() { return invoke( method: ActionMethod.closeConnections, ); } @override Future closeConnection(String id) { return invoke( method: ActionMethod.closeConnection, data: id, ); } @override Future getProfile(String id) { return invoke( method: ActionMethod.getProfile, data: id, ); } @override FutureOr getTotalTraffic() { return invoke( method: ActionMethod.getTotalTraffic, ); } @override FutureOr getTraffic() { return invoke( method: ActionMethod.getTraffic, ); } @override resetTraffic() { invoke(method: ActionMethod.resetTraffic); } @override startLog() { invoke(method: ActionMethod.startLog); } @override stopLog() { invoke( method: ActionMethod.stopLog, ); } @override Future startListener() { return invoke( method: ActionMethod.startListener, ); } @override stopListener() { return invoke( method: ActionMethod.stopListener, ); } @override Future asyncTestDelay(String url, String proxyName) { final delayParams = { "proxy-name": proxyName, "timeout": httpTimeoutDuration.inMilliseconds, "test-url": url, }; return invoke( method: ActionMethod.asyncTestDelay, data: json.encode(delayParams), timeout: Duration( milliseconds: 6000, ), onTimeout: () { return json.encode( Delay( name: proxyName, value: -1, url: url, ), ); }, ); } @override FutureOr getCountryCode(String ip) { return invoke( method: ActionMethod.getCountryCode, data: ip, ); } @override FutureOr getMemory() { return invoke( method: ActionMethod.getMemory, ); } }