Compare commits

..

5 Commits

Author SHA1 Message Date
chen08209
6dcb466fd3 Fix android proxy error 2024-07-01 20:57:24 +08:00
chen08209
acbcec358b Fix submit error 2024-07-01 19:51:11 +08:00
chen08209
a923549ddf Add windows tun
Optimize android proxy

Optimize change profile

Update application ua

Optimize delay test
2024-07-01 19:41:57 +08:00
chen08209
07bd21580b Fix android repeated request notification issues 2024-06-28 21:16:47 +08:00
chen08209
57ceb64a5e Fix memory overflow issues 2024-06-28 07:49:06 +08:00
26 changed files with 4804 additions and 116 deletions

View File

@@ -21,11 +21,25 @@ jobs:
os: macos-13
steps:
- name: Setup Mingw64
if: startsWith(matrix.platform,'windows')
uses: msys2/setup-msys2@v2
with:
msystem: mingw64
install: mingw-w64-x86_64-gcc
update: true
- name: Set Mingw64 Env
if: startsWith(matrix.platform,'windows')
run: |
echo "${{ runner.temp }}\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Check Matrix
run: |
echo "Running on ${{ matrix.os }}"
echo "Arch: ${{ runner.arch }}"
gcc --version
echo "Running on ${{ matrix.os }}"
echo "Arch: ${{ runner.arch }}"
gcc --version
- name: Checkout
uses: actions/checkout@v4
@@ -52,10 +66,10 @@ jobs:
if: startsWith(matrix.platform,'android')
run: |
echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/app/keystore.jks
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/local.properties
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/local.properties
echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> android/local.properties
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/local.properties
- name: Setup Go
uses: actions/setup-go@v5

View File

@@ -22,8 +22,10 @@
<application
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:networkSecurityConfig="@xml/network_security_config"
android:extractNativeLibs="true"
android:label="FlClash">
android:label="FlClash"
tools:targetApi="n">
<activity
android:name="com.follow.clash.MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"

View File

@@ -44,6 +44,7 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
private var props: Props? = null
private lateinit var title: String
private lateinit var content: String
var isBlockNotification: Boolean = false
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
@@ -135,7 +136,6 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
GlobalState.runTime = null;
}
@SuppressLint("ForegroundServiceType")
private fun startForeground() {
if (GlobalState.runState.value != RunState.START) return
flClashVpnService?.startForeground(title, content)
@@ -152,13 +152,14 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
if (permission == PackageManager.PERMISSION_GRANTED) {
startForeground()
} else {
activity?.let {
ActivityCompat.requestPermissions(
it,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
NOTIFICATION_PERMISSION_REQUEST_CODE
)
}
if (isBlockNotification) return
if (activity == null) return
ActivityCompat.requestPermissions(
activity!!,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
NOTIFICATION_PERMISSION_REQUEST_CODE
)
}
} else {
startForeground()
@@ -192,11 +193,14 @@ class ProxyPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAwar
grantResults: IntArray
): Boolean {
if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startForeground()
isBlockNotification = true
if (grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startForeground()
}
}
}
return true;
return false;
}

View File

@@ -7,4 +7,8 @@
<certificates src="user" />
</trust-anchors>
</base-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">localhost</domain>
<domain includeSubdomains="true">127.0.0.1</domain>
</domain-config>
</network-security-config>

View File

@@ -340,8 +340,8 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.Mode = patchConfig.Mode
targetConfig.Tun.Enable = patchConfig.Tun.Enable
targetConfig.Tun.Device = patchConfig.Tun.Device
//targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack
//targetConfig.Tun.Stack = patchConfig.Tun.Stack
targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack
targetConfig.Tun.Stack = patchConfig.Tun.Stack
targetConfig.GeodataLoader = patchConfig.GeodataLoader
targetConfig.Profile.StoreSelected = false
if targetConfig.DNS.Enable == false {
@@ -396,7 +396,6 @@ func applyConfig(isPatch bool) {
if isPatch {
patchConfig(cfg.General)
} else {
executor.Shutdown()
runtime.GC()
hub.UltraApplyConfig(cfg, true)
}

View File

@@ -1,6 +1,7 @@
package dart_bridge
/*
#include <stdlib.h>
#include "stdint.h"
#include "include/dart_api_dl.h"
#include "include/dart_api_dl.c"
@@ -28,6 +29,7 @@ func SendToPort(port int64, msg string) {
var obj C.Dart_CObject
obj._type = C.Dart_CObject_kString
msgString := C.CString(msg)
defer C.free(unsafe.Pointer(msgString))
ptr := unsafe.Pointer(&obj.value[0])
*(**C.char)(ptr) = msgString
isSuccess := C.GoDart_PostCObject(C.Dart_Port_DL(port), &obj)

View File

@@ -1,5 +1,8 @@
package main
/*
#include <stdlib.h>
*/
import "C"
import (
bridge "core/dart-bridge"
@@ -71,8 +74,8 @@ func forceGc() {
//export validateConfig
func validateConfig(s *C.char, port C.longlong) {
i := int64(port)
bytes := []byte(C.GoString(s))
go func() {
bytes := []byte(C.GoString(s))
_, err := config.UnmarshalRawConfig(bytes)
if err != nil {
bridge.SendToPort(i, err.Error())
@@ -85,8 +88,8 @@ func validateConfig(s *C.char, port C.longlong) {
//export updateConfig
func updateConfig(s *C.char, port C.longlong) {
i := int64(port)
paramsString := C.GoString(s)
go func() {
paramsString := C.GoString(s)
var params = &GenerateConfigParams{}
err := json.Unmarshal([]byte(paramsString), params)
if err != nil {
@@ -147,30 +150,32 @@ func getProxies() *C.char {
}
//export changeProxy
func changeProxy(s *C.char) bool {
func changeProxy(s *C.char) {
paramsString := C.GoString(s)
go func() {
paramsString := C.GoString(s)
var params = &ChangeProxyParams{}
err := json.Unmarshal([]byte(paramsString), params)
if err != nil {
log.Infoln("Unmarshal ChangeProxyParams %v", err)
}
groupName := *params.GroupName
proxyName := *params.ProxyName
proxies := tunnel.ProxiesWithProviders()
proxy := proxies[*params.GroupName]
if proxy == nil {
group := proxies[groupName]
if group == nil {
return
}
log.Infoln("change proxy %s", proxy.Name())
adapterProxy := proxy.(*adapter.Proxy)
adapterProxy := group.(*adapter.Proxy)
selector, ok := adapterProxy.ProxyAdapter.(*outboundgroup.Selector)
if !ok {
return
}
if err := selector.Set(*params.ProxyName); err != nil {
return
err = selector.Set(proxyName)
if err == nil {
log.Infoln("[Selector] %s selected %s", groupName, proxyName)
}
}()
return true
}
//export getTraffic
@@ -211,8 +216,8 @@ func resetTraffic() {
//export asyncTestDelay
func asyncTestDelay(s *C.char, port C.longlong) {
i := int64(port)
paramsString := C.GoString(s)
go func() {
paramsString := C.GoString(s)
var params = &TestDelayParams{}
err := json.Unmarshal([]byte(paramsString), params)
if err != nil {
@@ -296,7 +301,6 @@ func closeConnections() bool {
//export closeConnection
func closeConnection(id *C.char) bool {
connectionId := C.GoString(id)
err := statistic.DefaultManager.Get(connectionId).Close()
if err != nil {
return false
@@ -307,10 +311,13 @@ func closeConnection(id *C.char) bool {
//export getProviders
func getProviders() *C.char {
data, err := json.Marshal(tunnel.Providers())
var msg *C.char
if err != nil {
return C.CString("")
msg = C.CString("")
return msg
}
return C.CString(string(data))
msg = C.CString(string(data))
return msg
}
//export getProvider
@@ -360,10 +367,9 @@ func getExternalProviders() *C.char {
//export updateExternalProvider
func updateExternalProvider(providerName *C.char, providerType *C.char, port C.longlong) {
i := int64(port)
providerNameString := C.GoString(providerName)
providerTypeString := C.GoString(providerType)
go func() {
providerNameString := C.GoString(providerName)
providerTypeString := C.GoString(providerType)
switch providerTypeString {
case "Proxy":
providers := tunnel.Providers()
@@ -409,6 +415,11 @@ func initNativeApiBridge(api unsafe.Pointer, port C.longlong) {
bridge.Port = &i
}
//export freeCString
func freeCString(s *C.char) {
C.free(unsafe.Pointer(s))
}
func init() {
provider.HealthcheckHook = func(name string, delay uint16) {
delayData := &Delay{

View File

@@ -71,8 +71,8 @@ func setProcessMap(s *C.char) {
if s == nil {
return
}
paramsString := C.GoString(s)
go func() {
paramsString := C.GoString(s)
var processMapItem = &ProcessMapItem{}
err := json.Unmarshal([]byte(paramsString), processMapItem)
if err == nil {

View File

@@ -18,6 +18,21 @@ import (
var tunLock sync.Mutex
var tun *t.Tun
type FdMap struct {
m sync.Map
}
func (cm *FdMap) Store(key int) {
cm.m.Store(key, struct{}{})
}
func (cm *FdMap) Load(key int) bool {
_, ok := cm.m.Load(key)
return ok
}
var fdMap FdMap
//export startTUN
func startTUN(fd C.int) {
go func() {
@@ -63,16 +78,38 @@ func stopTun() {
var errBlocked = errors.New("blocked")
//export setFdMap
func setFdMap(fd C.long) {
fdInt := int(fd)
go func() {
fdMap.Store(fdInt)
}()
}
func init() {
dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error {
if platform.ShouldBlockConnection() {
return errBlocked
}
return conn.Control(func(fd uintptr) {
fdInt := int(fd)
//timeout := time.After(100 * time.Millisecond)
if tun != nil {
tun.MarkSocket(int(fd))
time.Sleep(time.Millisecond * 100)
tun.MarkSocket(fdInt)
time.Sleep(100 * time.Millisecond)
}
//for {
// select {
// case <-timeout:
// return
// default:
// exists := fdMap.Load(fdInt)
// if exists {
// return
// }
// time.Sleep(20 * time.Millisecond)
// }
//}
})
}
}

View File

@@ -45,10 +45,10 @@ class ClashCore {
}
bool init(String homeDir) {
return clashFFI.initClash(
homeDir.toNativeUtf8().cast(),
) ==
1;
final homeDirChar = homeDir.toNativeUtf8().cast<Char>();
final isInit = clashFFI.initClash(homeDirChar) == 1;
malloc.free(homeDirChar);
return isInit;
}
shutdown() {
@@ -67,10 +67,12 @@ class ClashCore {
receiver.close();
}
});
final dataChar = data.toNativeUtf8().cast<Char>();
clashFFI.validateConfig(
data.toNativeUtf8().cast(),
dataChar,
receiver.sendPort.nativePort,
);
malloc.free(dataChar);
return completer.future;
}
@@ -84,20 +86,23 @@ class ClashCore {
}
});
final params = json.encode(updateConfigParams);
final paramsChar = params.toNativeUtf8().cast<Char>();
clashFFI.updateConfig(
params.toNativeUtf8().cast(),
paramsChar,
receiver.sendPort.nativePort,
);
malloc.free(paramsChar);
return completer.future;
}
Future<List<Group>> getProxiesGroups() {
final proxiesRaw = clashFFI.getProxies();
final proxiesRawString = proxiesRaw.cast<Utf8>().toDartString();
clashFFI.freeCString(proxiesRaw);
return Isolate.run<List<Group>>(() {
if(proxiesRawString.isEmpty) return [];
if (proxiesRawString.isEmpty) return [];
final proxies = (json.decode(proxiesRawString) ?? {}) as Map;
if(proxies.isEmpty) return [];
if (proxies.isEmpty) return [];
final groupNames = [
UsedProxy.GLOBAL.name,
...(proxies[UsedProxy.GLOBAL.name]["all"] as List).where((e) {
@@ -111,7 +116,8 @@ class ClashCore {
group["all"] = ((group["all"] ?? []) as List)
.map(
(name) => proxies[name],
)
)
.where((proxy) => proxy != null)
.toList();
return group;
}).toList();
@@ -122,14 +128,15 @@ class ClashCore {
Future<List<ExternalProvider>> getExternalProviders() {
final externalProvidersRaw = clashFFI.getExternalProviders();
final externalProvidersRawString =
externalProvidersRaw.cast<Utf8>().toDartString();
externalProvidersRaw.cast<Utf8>().toDartString();
clashFFI.freeCString(externalProvidersRaw);
return Isolate.run<List<ExternalProvider>>(() {
final externalProviders =
(json.decode(externalProvidersRawString) as List<dynamic>)
.map(
(item) => ExternalProvider.fromJson(item),
)
.toList();
(json.decode(externalProvidersRawString) as List<dynamic>)
.map(
(item) => ExternalProvider.fromJson(item),
)
.toList();
return externalProviders;
});
}
@@ -146,17 +153,24 @@ class ClashCore {
receiver.close();
}
});
final providerNameChar = providerName.toNativeUtf8().cast<Char>();
final providerTypeChar = providerType.toNativeUtf8().cast<Char>();
clashFFI.updateExternalProvider(
providerName.toNativeUtf8().cast(),
providerType.toNativeUtf8().cast(),
providerNameChar,
providerTypeChar,
receiver.sendPort.nativePort,
);
malloc.free(providerNameChar);
malloc.free(providerTypeChar);
return completer.future;
}
bool changeProxy(ChangeProxyParams changeProxyParams) {
final params = json.encode(changeProxyParams);
return clashFFI.changeProxy(params.toNativeUtf8().cast()) == 1;
final paramsChar = params.toNativeUtf8().cast<Char>();
final isInit = clashFFI.changeProxy(paramsChar) == 1;
malloc.free(paramsChar);
return isInit;
}
Future<Delay> getDelay(String proxyName) {
@@ -172,13 +186,16 @@ class ClashCore {
receiver.close();
}
});
final delayParamsChar =
json.encode(delayParams).toNativeUtf8().cast<Char>();
clashFFI.asyncTestDelay(
json.encode(delayParams).toNativeUtf8().cast(),
delayParamsChar,
receiver.sendPort.nativePort,
);
malloc.free(delayParamsChar);
Future.delayed(httpTimeoutDuration + moreDuration, () {
receiver.close();
if(!completer.isCompleted){
if (!completer.isCompleted) {
completer.complete(
Delay(name: proxyName, value: -1),
);
@@ -188,28 +205,33 @@ class ClashCore {
}
clearEffect(String path) {
clashFFI.clearEffect(path.toNativeUtf8().cast());
final pathChar = path.toNativeUtf8().cast<Char>();
clashFFI.clearEffect(pathChar);
malloc.free(pathChar);
}
VersionInfo getVersionInfo() {
final versionInfoRaw = clashFFI.getVersionInfo();
final versionInfo = json.decode(versionInfoRaw.cast<Utf8>().toDartString());
clashFFI.freeCString(versionInfoRaw);
return VersionInfo.fromJson(versionInfo);
}
Traffic getTraffic() {
final trafficRaw = clashFFI.getTraffic();
final trafficMap = json.decode(trafficRaw.cast<Utf8>().toDartString());
clashFFI.freeCString(trafficRaw);
return Traffic.fromMap(trafficMap);
}
Traffic getTotalTraffic() {
final trafficRaw = clashFFI.getTotalTraffic();
final trafficMap = json.decode(trafficRaw.cast<Utf8>().toDartString());
clashFFI.freeCString(trafficRaw);
return Traffic.fromMap(trafficMap);
}
void resetTraffic(){
void resetTraffic() {
clashFFI.resetTraffic();
}
@@ -234,7 +256,14 @@ class ClashCore {
}
void setProcessMap(ProcessMapItem processMapItem) {
clashFFI.setProcessMap(json.encode(processMapItem).toNativeUtf8().cast());
final processMapItemChar =
json.encode(processMapItem).toNativeUtf8().cast<Char>();
clashFFI.setProcessMap(processMapItemChar);
malloc.free(processMapItemChar);
}
void setFdMap(int fd) {
clashFFI.setFdMap(fd);
}
// DateTime? getRunTime() {
@@ -246,13 +275,16 @@ class ClashCore {
List<Connection> getConnections() {
final connectionsDataRaw = clashFFI.getConnections();
final connectionsData =
json.decode(connectionsDataRaw.cast<Utf8>().toDartString()) as Map;
json.decode(connectionsDataRaw.cast<Utf8>().toDartString()) as Map;
clashFFI.freeCString(connectionsDataRaw);
final connectionsRaw = connectionsData['connections'] as List? ?? [];
return connectionsRaw.map((e) => Connection.fromJson(e)).toList();
}
closeConnections(String id) {
clashFFI.closeConnection(id.toNativeUtf8().cast());
final idChar = id.toNativeUtf8().cast<Char>();
clashFFI.closeConnection(idChar);
malloc.free(idChar);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,35 @@
import 'dart:async';
import 'dart:io';
import 'package:package_info_plus/package_info_plus.dart';
class AppPackage{
import 'common.dart';
class AppPackage {
static AppPackage? _instance;
Completer<PackageInfo> packageInfoCompleter = Completer();
AppPackage._internal() {
PackageInfo.fromPlatform().then(
(value) => packageInfoCompleter.complete(value),
PackageInfo.fromPlatform().then(
(value) => packageInfoCompleter.complete(value),
);
}
Future<String> getUa() async {
final packageInfo = await packageInfoCompleter.future;
final uas = [
"$appName/v${packageInfo.version}",
"clash-verge/v1.6.6",
"Platform/${Platform.operatingSystem}",
];
return uas.join(" ");
}
factory AppPackage() {
_instance ??= AppPackage._internal();
return _instance!;
}
}
final appPackage = AppPackage();
final appPackage = AppPackage();

View File

@@ -2,21 +2,20 @@ import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/ip.dart';
import 'package:fl_clash/state.dart';
import 'constant.dart';
import 'other.dart';
import 'package.dart';
class Request {
late final Dio _dio;
int? _port;
bool _isStart = false;
Request() {
_dio = Dio(
BaseOptions(
headers: {"User-Agent": coreName},
),
);
_dio = Dio();
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
_syncProxy();
@@ -45,10 +44,14 @@ class Request {
}
Future<Response> getFileResponseForUrl(String url) async {
final ua = await appPackage.getUa();
final response = await _dio
.get(
url,
options: Options(
headers: {
"User-Agent": ua,
},
responseType: ResponseType.bytes,
),
)

View File

@@ -10,5 +10,5 @@ extension TextStyleExtension on TextStyle {
TextStyle get toBold => copyWith(fontWeight: FontWeight.bold);
TextStyle get toMinus => copyWith(fontSize: fontSize! - 1);
TextStyle get toMinus => copyWith(fontSize: fontSize! - 2);
}

View File

@@ -337,7 +337,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
Widget _buildMoreSection() {
final items = [
if (false)
if (system.isDesktop)
Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.tun.enable,
builder: (_, tunEnable, __) {
@@ -361,7 +361,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
];
if(items.isEmpty) return Container();
return Section(
title: appLocalizations.general,
title: appLocalizations.more,
child: Column(
children: [
for (final item in items) ...[

View File

@@ -53,7 +53,7 @@ class _IntranetIPState extends State<IntranetIP> {
},
child: Container(
padding: const EdgeInsets.all(16).copyWith(top: 0),
height: globalState.appController.measure.titleLargeHeight + 24 - 1,
height: globalState.appController.measure.titleLargeHeight + 24 - 2,
child: ValueListenableBuilder(
valueListenable: ipNotifier,
builder: (_, value, __) {

View File

@@ -137,7 +137,7 @@ class _NetworkDetectionState extends State<NetworkDetection> {
Container(
height: globalState.appController.measure.titleLargeHeight +
24 -
1,
2,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.all(16).copyWith(top: 0),
child: FadeBox(

View File

@@ -3,6 +3,7 @@ import 'package:fl_clash/fragments/profiles/edit_profile.dart';
import 'package:fl_clash/fragments/profiles/view_profile.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/plugins/app.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
@@ -28,6 +29,7 @@ class ProfilesFragment extends StatefulWidget {
class _ProfilesFragmentState extends State<ProfilesFragment> {
final hasPadding = ValueNotifier<bool>(false);
Function? applyConfigDebounce;
List<GlobalObjectKey<_ProfileItemState>> profileItemKeys = [];
@@ -86,12 +88,30 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
);
}
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
hasPadding.dispose();
}
_changeProfile(String? id) async {
final appController = globalState.appController;
final config = appController.config;
if (id == config.currentProfileId) return;
config.currentProfileId = id;
applyConfigDebounce ??= debounce<Function()>(() async {
await appController.applyProfile();
appController.appState.delayMap = {};
appController.saveConfigPreferences();
});
applyConfigDebounce!();
}
@override
Widget build(BuildContext context) {
return Selector<AppState, bool>(
@@ -154,8 +174,7 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
key: profileItemKeys[i],
profile: state.profiles[i],
groupValue: state.currentProfileId,
onChanged:
globalState.appController.changeProfile,
onChanged: _changeProfile,
),
),
],

View File

@@ -831,7 +831,12 @@ class ProxyCard extends StatelessWidget {
groupName,
proxy.name,
);
appController.changeProxy();
clashCore.changeProxy(
ChangeProxyParams(
groupName: groupName,
proxyName: proxy.name,
),
);
}
@override

View File

@@ -49,11 +49,15 @@ Future<void> vpnService() async {
isCompatible: config.isCompatible,
selectedMap: config.currentSelectedMap,
);
clashMessage.addListener(ClashMessageListenerWithVpn(onTun: (String fd) {
proxyManager.setProtect(
int.parse(fd),
);
}));
clashMessage.addListener(
ClashMessageListenerWithVpn(
onTun: (String fd) async {
final fdInt = int.parse(fd);
await proxyManager.setProtect(fdInt);
clashCore.setFdMap(fdInt);
},
),
);
await globalState.init(
appState: appState,

View File

@@ -123,9 +123,10 @@ class AppState with ChangeNotifier {
final index = groups.indexWhere((element) => element.name == proxyName);
if (index == -1) return proxyName;
final group = groups[index];
return getRealProxyName(selectedMap.containsKey(proxyName)
? selectedMap[proxyName]
: group.now);
return getRealProxyName((selectedMap.containsKey(proxyName)
? selectedMap[proxyName]
: group.now)) ??
proxyName;
}
String? get showProxyName {

View File

@@ -1,5 +1,7 @@
// ignore_for_file: invalid_annotation_target
import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/common/constant.dart';
import 'package:flutter/material.dart';
@@ -235,7 +237,12 @@ class ClashConfig extends ChangeNotifier {
}
}
Tun get tun => _tun;
Tun get tun {
if (Platform.isAndroid) {
return _tun.copyWith(enable: false);
}
return _tun;
}
set tun(Tun value) {
if (_tun != value) {
@@ -282,9 +289,4 @@ class ClashConfig extends ChangeNotifier {
factory ClashConfig.fromJson(Map<String, dynamic> json) {
return _$ClashConfigFromJson(json);
}
@override
String toString() {
return 'ClashConfig{_mixedPort: $_mixedPort, _allowLan: $_allowLan, _mode: $_mode, _logLevel: $_logLevel, _tun: $_tun, _dns: $_dns, _rules: $_rules}';
}
}

View File

@@ -43,13 +43,14 @@ class GlobalState {
}) async {
final profilePath = await appPath.getProfilePath(config.currentProfileId);
await config.currentProfile?.checkAndUpdate();
debugPrint("update config");
final res = await clashCore.updateConfig(UpdateConfigParams(
profilePath: profilePath,
config: clashConfig,
isPatch: isPatch,
isCompatible: config.isCompatible,
));
final res = await clashCore.updateConfig(
UpdateConfigParams(
profilePath: profilePath,
config: clashConfig,
isPatch: isPatch,
isCompatible: config.isCompatible,
),
);
if (res.isNotEmpty) throw res;
}
@@ -263,7 +264,7 @@ class GlobalState {
}
}
int getColumns(ViewMode viewMode,int currentColumns){
int getColumns(ViewMode viewMode, int currentColumns) {
final targetColumnsArray = switch (viewMode) {
ViewMode.mobile => [2, 1],
ViewMode.laptop => [3, 2],

View File

@@ -51,8 +51,10 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
}
@override
void onTun(String fd) {
proxyManager.setProtect(int.parse(fd));
Future<void> onTun(String fd) async {
final fdInt = int.parse(fd);
await proxyManager.setProtect(fdInt);
clashCore.setFdMap(fdInt);
super.onTun(fd);
}
@@ -73,10 +75,4 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
globalState.appController.appState.addRequest(connection);
super.onRequest(connection);
}
@override
void onRun(String runTime) async {
// proxy?.updateStartTime();
super.onRun(runTime);
}
}

View File

@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.30
version: 0.8.33+202407012
environment:
sdk: '>=3.1.0 <4.0.0'