Compare commits

...

3 Commits

Author SHA1 Message Date
chen08209
ec2890cab2 Fix windows error 2024-07-18 17:27:29 +08:00
chen08209
ca946c1b06 Fix setup.dart error 2024-07-18 16:39:28 +08:00
chen08209
3bc3172723 Fix android system proxy not effective
Add macos arm64
2024-07-18 16:33:53 +08:00
19 changed files with 189 additions and 86 deletions

View File

@@ -15,10 +15,16 @@ jobs:
os: ubuntu-latest
- platform: windows
os: windows-latest
arch: amd64
- platform: linux
os: ubuntu-latest
arch: amd64
- platform: macos
os: macos-13
arch: amd64
- platform: macos
os: macos-latest
arch: arm64
steps:
- name: Setup Mingw64
@@ -89,13 +95,12 @@ jobs:
run: flutter pub get
- name: Setup
run: |
dart setup.dart ${{ matrix.platform }}
run: dart setup.dart ${{ matrix.platform }} ${{ matrix.arch && format('--arch {0}', matrix.arch) }}
- name: Upload
uses: actions/upload-artifact@v4
with:
name: artifact-${{ matrix.platform }}
name: artifact-${{ matrix.platform }}${{ matrix.arch && format('-{0}', matrix.arch) }}
path: ./dist
retention-days: 1
overwrite: true

43
core/status.go Normal file
View File

@@ -0,0 +1,43 @@
//go:build android
package main
import "C"
import (
"encoding/json"
"fmt"
)
type AccessControl struct {
Mode string `json:"mode"`
AcceptList []string `json:"acceptList"`
RejectList []string `json:"rejectList"`
IsFilterSystemApp bool `json:"isFilterSystemApp"`
}
type AndroidProps struct {
AccessControl *AccessControl `json:"accessControl"`
AllowBypass bool `json:"allowBypass"`
SystemProxy bool `json:"systemProxy"`
}
var androidProps AndroidProps
//export getAndroidProps
func getAndroidProps() *C.char {
data, err := json.Marshal(androidProps)
if err != nil {
fmt.Println("Error:", err)
return C.CString("")
}
return C.CString(string(data))
}
//export setAndroidProps
func setAndroidProps(s *C.char) {
paramsString := C.GoString(s)
err := json.Unmarshal([]byte(paramsString), &androidProps)
if err != nil {
return
}
}

View File

@@ -237,6 +237,21 @@ class ClashCore {
return VersionInfo.fromJson(versionInfo);
}
setProps(Props props) {
final propsChar = json.encode(props).toNativeUtf8().cast<Char>();
clashFFI.setAndroidProps(propsChar);
malloc.free(propsChar);
}
Props getProps() {
final androidPropsRaw = clashFFI.getAndroidProps();
final androidProps = json.decode(
androidPropsRaw.cast<Utf8>().toDartString(),
);
clashFFI.freeCString(androidPropsRaw);
return Props.fromJson(androidProps);
}
Traffic getTraffic() {
final trafficRaw = clashFFI.getTraffic();
final trafficMap = json.decode(trafficRaw.cast<Utf8>().toDartString());

View File

@@ -5499,6 +5499,30 @@ class ClashFFI {
late final _setProcessMap =
_setProcessMapPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
ffi.Pointer<ffi.Char> getAndroidProps() {
return _getAndroidProps();
}
late final _getAndroidPropsPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(
'getAndroidProps');
late final _getAndroidProps =
_getAndroidPropsPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
void setAndroidProps(
ffi.Pointer<ffi.Char> s,
) {
return _setAndroidProps(
s,
);
}
late final _setAndroidPropsPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(
'setAndroidProps');
late final _setAndroidProps =
_setAndroidPropsPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
void startTUN(
int fd,
int port,

View File

@@ -15,8 +15,8 @@ class ProxyManager {
DateTime? get startTime => _proxy.startTime;
Future<bool?> startProxy({required int port, String? args}) async {
return await _proxy.startProxy(port, args);
Future<bool?> startProxy({required int port}) async {
return await _proxy.startProxy(port);
}
Future<bool?> stopProxy() async {

View File

@@ -67,6 +67,7 @@ class _ProxiesTabFragmentState extends State<ProxiesTabFragment>
return SizedBox(
width: double.infinity,
child: Wrap(
alignment: WrapAlignment.center,
runSpacing: 8,
spacing: 8,
children: [

View File

@@ -29,8 +29,8 @@ class AccessControl with _$AccessControl {
class Props with _$Props {
const factory Props({
AccessControl? accessControl,
bool? allowBypass,
bool? systemProxy,
required bool allowBypass,
required bool systemProxy,
}) = _Props;
factory Props.fromJson(Map<String, Object?> json) => _$PropsFromJson(json);

View File

@@ -247,8 +247,8 @@ Props _$PropsFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$Props {
AccessControl? get accessControl => throw _privateConstructorUsedError;
bool? get allowBypass => throw _privateConstructorUsedError;
bool? get systemProxy => throw _privateConstructorUsedError;
bool get allowBypass => throw _privateConstructorUsedError;
bool get systemProxy => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@@ -260,8 +260,7 @@ abstract class $PropsCopyWith<$Res> {
factory $PropsCopyWith(Props value, $Res Function(Props) then) =
_$PropsCopyWithImpl<$Res, Props>;
@useResult
$Res call(
{AccessControl? accessControl, bool? allowBypass, bool? systemProxy});
$Res call({AccessControl? accessControl, bool allowBypass, bool systemProxy});
$AccessControlCopyWith<$Res>? get accessControl;
}
@@ -280,22 +279,22 @@ class _$PropsCopyWithImpl<$Res, $Val extends Props>
@override
$Res call({
Object? accessControl = freezed,
Object? allowBypass = freezed,
Object? systemProxy = freezed,
Object? allowBypass = null,
Object? systemProxy = null,
}) {
return _then(_value.copyWith(
accessControl: freezed == accessControl
? _value.accessControl
: accessControl // ignore: cast_nullable_to_non_nullable
as AccessControl?,
allowBypass: freezed == allowBypass
allowBypass: null == allowBypass
? _value.allowBypass
: allowBypass // ignore: cast_nullable_to_non_nullable
as bool?,
systemProxy: freezed == systemProxy
as bool,
systemProxy: null == systemProxy
? _value.systemProxy
: systemProxy // ignore: cast_nullable_to_non_nullable
as bool?,
as bool,
) as $Val);
}
@@ -319,8 +318,7 @@ abstract class _$$PropsImplCopyWith<$Res> implements $PropsCopyWith<$Res> {
__$$PropsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{AccessControl? accessControl, bool? allowBypass, bool? systemProxy});
$Res call({AccessControl? accessControl, bool allowBypass, bool systemProxy});
@override
$AccessControlCopyWith<$Res>? get accessControl;
@@ -338,22 +336,22 @@ class __$$PropsImplCopyWithImpl<$Res>
@override
$Res call({
Object? accessControl = freezed,
Object? allowBypass = freezed,
Object? systemProxy = freezed,
Object? allowBypass = null,
Object? systemProxy = null,
}) {
return _then(_$PropsImpl(
accessControl: freezed == accessControl
? _value.accessControl
: accessControl // ignore: cast_nullable_to_non_nullable
as AccessControl?,
allowBypass: freezed == allowBypass
allowBypass: null == allowBypass
? _value.allowBypass
: allowBypass // ignore: cast_nullable_to_non_nullable
as bool?,
systemProxy: freezed == systemProxy
as bool,
systemProxy: null == systemProxy
? _value.systemProxy
: systemProxy // ignore: cast_nullable_to_non_nullable
as bool?,
as bool,
));
}
}
@@ -361,7 +359,10 @@ class __$$PropsImplCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$PropsImpl implements _Props {
const _$PropsImpl({this.accessControl, this.allowBypass, this.systemProxy});
const _$PropsImpl(
{this.accessControl,
required this.allowBypass,
required this.systemProxy});
factory _$PropsImpl.fromJson(Map<String, dynamic> json) =>
_$$PropsImplFromJson(json);
@@ -369,9 +370,9 @@ class _$PropsImpl implements _Props {
@override
final AccessControl? accessControl;
@override
final bool? allowBypass;
final bool allowBypass;
@override
final bool? systemProxy;
final bool systemProxy;
@override
String toString() {
@@ -413,17 +414,17 @@ class _$PropsImpl implements _Props {
abstract class _Props implements Props {
const factory _Props(
{final AccessControl? accessControl,
final bool? allowBypass,
final bool? systemProxy}) = _$PropsImpl;
required final bool allowBypass,
required final bool systemProxy}) = _$PropsImpl;
factory _Props.fromJson(Map<String, dynamic> json) = _$PropsImpl.fromJson;
@override
AccessControl? get accessControl;
@override
bool? get allowBypass;
bool get allowBypass;
@override
bool? get systemProxy;
bool get systemProxy;
@override
@JsonKey(ignore: true)
_$$PropsImplCopyWith<_$PropsImpl> get copyWith =>

View File

@@ -132,8 +132,8 @@ _$PropsImpl _$$PropsImplFromJson(Map<String, dynamic> json) => _$PropsImpl(
? null
: AccessControl.fromJson(
json['accessControl'] as Map<String, dynamic>),
allowBypass: json['allowBypass'] as bool?,
systemProxy: json['systemProxy'] as bool?,
allowBypass: json['allowBypass'] as bool,
systemProxy: json['systemProxy'] as bool,
);
Map<String, dynamic> _$$PropsImplToJson(_$PropsImpl instance) =>

View File

@@ -37,7 +37,7 @@ class Proxy extends ProxyPlatform {
return _instance!;
}
Future<bool?> _initService() async {
Future<bool?> initService() async {
return await methodChannel.invokeMethod<bool>("initService");
}
@@ -46,12 +46,11 @@ class Proxy extends ProxyPlatform {
}
@override
Future<bool?> startProxy(port, args) async {
if (!globalState.isVpnService) {
return await _initService();
}
return await methodChannel
.invokeMethod<bool>("startProxy", {'port': port, 'args': args});
Future<bool?> startProxy(port) async {
return await methodChannel.invokeMethod<bool>("startProxy", {
'port': port,
'args': json.encode(clashCore.getProps()),
});
}
@override

View File

@@ -1,10 +1,8 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:animations/animations.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/plugins/proxy.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
@@ -72,18 +70,20 @@ class GlobalState {
required Config config,
required ClashConfig clashConfig,
}) async {
final args = config.isAccessControl
? json.encode(
Props(
accessControl: config.accessControl,
allowBypass: config.allowBypass,
),
)
: null;
await proxyManager.startProxy(
port: clashConfig.mixedPort,
args: args,
);
if (!globalState.isVpnService && Platform.isAndroid) {
clashCore.setProps(
Props(
accessControl: config.isAccessControl ? config.accessControl : null,
allowBypass: config.allowBypass,
systemProxy: config.systemProxy,
),
);
await proxy?.initService();
} else {
await proxyManager.startProxy(
port: clashConfig.mixedPort,
);
}
startListenUpdate();
if (Platform.isAndroid) {
return;
@@ -123,6 +123,15 @@ class GlobalState {
}) async {
appState.isInit = clashCore.isInit;
if (!appState.isInit) {
if(Platform.isAndroid){
clashCore.setProps(
Props(
accessControl: config.isAccessControl ? config.accessControl : null,
allowBypass: config.allowBypass,
systemProxy: config.systemProxy,
),
);
}
appState.isInit = await clashService.init(
config: config,
clashConfig: clashConfig,

View File

@@ -118,7 +118,7 @@ install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# libclash.so
set(CLASH_DIR "../libclash/linux/amd64")
set(CLASH_DIR "../libclash/linux")
install(FILES "${CLASH_DIR}/libclash.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)

View File

@@ -87,7 +87,7 @@
4121E8CCDC7DC35194714CDE /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
72CBDF47BB69EDEFE644C48D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
779829C96DE7998FCC810C37 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
7AC277A92B90DE1400E026B1 /* libclash.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libclash.dylib; path = ../libclash/macos/amd64/libclash.dylib; sourceTree = "<group>"; };
7AC277A92B90DE1400E026B1 /* libclash.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libclash.dylib; path = ../libclash/macos/libclash.dylib; sourceTree = "<group>"; };
7AF070893C29500AB9129D89 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
7D929F2AFD80E155D78F3718 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -582,7 +582,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/amd64/";
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/";
PRODUCT_BUNDLE_IDENTIFIER = com.clash.follow;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
@@ -710,7 +710,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/amd64/";
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/";
PRODUCT_BUNDLE_IDENTIFIER = com.clash.follow;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -732,7 +732,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/amd64/";
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/";
PRODUCT_BUNDLE_IDENTIFIER = com.clash.follow;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;

View File

@@ -9,7 +9,7 @@ class Proxy extends ProxyPlatform {
static String url = "127.0.0.1";
@override
Future<bool?> startProxy(int port, String? args) async {
Future<bool?> startProxy(int port) async {
bool? isStart = false;
switch (Platform.operatingSystem) {
case "macos":
@@ -19,7 +19,7 @@ class Proxy extends ProxyPlatform {
isStart = await _startProxyWithLinux(port);
break;
case "windows":
isStart = await ProxyPlatform.instance.startProxy(port, args);
isStart = await ProxyPlatform.instance.startProxy(port);
break;
}
if (isStart == true) {

View File

@@ -12,7 +12,7 @@ class MethodChannelProxy extends ProxyPlatform {
MethodChannelProxy();
@override
Future<bool?> startProxy(int port, String? args) async {
Future<bool?> startProxy(int port) async {
return await methodChannel.invokeMethod<bool>("StartProxy", {'port': port});
}

View File

@@ -22,7 +22,7 @@ abstract class ProxyPlatform extends PlatformInterface {
DateTime? startTime;
Future<bool?> startProxy(int port, String? args) {
Future<bool?> startProxy(int port) {
throw UnimplementedError('startProxy() has not been implemented.');
}

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.41+202407171
version: 0.8.43+202407182
environment:
sdk: '>=3.1.0 <4.0.0'

View File

@@ -47,6 +47,11 @@ class BuildLibItem {
}
return platform.name;
}
@override
String toString() {
return 'BuildLibItem{platform: $platform, arch: $arch, archName: $archName}';
}
}
class Build {
@@ -54,12 +59,22 @@ class Build {
BuildLibItem(
platform: PlatformType.macos,
arch: Arch.amd64,
archName: 'amd64',
archName: '',
),
BuildLibItem(
platform: PlatformType.macos,
arch: Arch.arm64,
archName: '',
),
BuildLibItem(
platform: PlatformType.windows,
arch: Arch.amd64,
archName: 'amd64',
archName: '',
),
BuildLibItem(
platform: PlatformType.windows,
arch: Arch.arm64,
archName: '',
),
BuildLibItem(
platform: PlatformType.android,
@@ -79,7 +94,7 @@ class Build {
BuildLibItem(
platform: PlatformType.linux,
arch: Arch.amd64,
archName: 'amd64',
archName: '',
),
];
@@ -149,9 +164,8 @@ class Build {
}) async {
final items = buildItems.where(
(element) {
return element.platform == platform && arch == null
? true
: element.arch == arch;
return element.platform == platform &&
(arch == null ? true : element.arch == arch);
},
).toList();
for (final item in items) {
@@ -173,10 +187,6 @@ class Build {
env["GOARCH"] = item.arch.name;
env["CGO_ENABLED"] = "1";
env["CC"] = _getCc(item);
if (item.platform == PlatformType.macos) {
env["CGO_CFLAGS"] = "-mmacosx-version-min=10.11";
env["CGO_LDFLAGS"] = "-mmacosx-version-min=10.11";
}
await exec(
[
@@ -337,6 +347,9 @@ class BuildCommand extends Command {
final currentArches =
arches.where((element) => element.name == archName).toList();
final arch = currentArches.isEmpty ? null : currentArches.first;
if (arch == null && platform == PlatformType.windows) {
throw "Invalid arch";
}
await _buildLib(arch);
if (build != "all") {
return;
@@ -346,17 +359,15 @@ class BuildCommand extends Command {
_buildDistributor(
platform: platform,
targets: "exe,zip",
args: "--description amd64",
args: "--description ${arch!.name}",
);
break;
case PlatformType.linux:
await _getLinuxDependencies();
_buildDistributor(
platform: platform,
targets: "appimage,deb,rpm",
args: "--description amd64",
args: "--description ${arch!.name}",
);
break;
case PlatformType.android:
final targetMap = {
Arch.arm: "android-arm",
@@ -374,15 +385,13 @@ class BuildCommand extends Command {
args:
"--flutter-build-args split-per-abi --build-target-platform ${defaultTargets.join(",")}",
);
break;
case PlatformType.macos:
await _getMacosDependencies();
_buildDistributor(
platform: platform,
targets: "dmg",
args: "--description amd64",
args: "--description ${arch!.name}",
);
break;
}
}
}
@@ -399,8 +408,5 @@ main(args) async {
if (Platform.isMacOS) {
runner.addCommand(BuildCommand(platform: PlatformType.macos));
}
if (args.isEmpty) {
args = [Platform.operatingSystem];
}
runner.run(args);
}

View File

@@ -82,7 +82,7 @@ install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# libclash.so
set(CLASH_DIR "../libclash/windows/amd64")
set(CLASH_DIR "../libclash/windows")
# if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
# elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM" OR CMAKE_SYSTEM_PROCESSOR MATCHES "armv[0-9]+")