Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c3a0c576d | ||
|
|
6dcb466fd3 | ||
|
|
acbcec358b | ||
|
|
a923549ddf |
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -136,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)
|
||||
|
||||
@@ -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>
|
||||
Submodule core/Clash.Meta updated: 48425d7cf9...5766f0b026
@@ -4,6 +4,7 @@ import "C"
|
||||
import (
|
||||
"github.com/metacubex/mihomo/adapter"
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
||||
ap "github.com/metacubex/mihomo/adapter/provider"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
@@ -59,11 +60,17 @@ type ruleProviderSchema struct {
|
||||
Interval int `provider:"interval,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigExtendedParams struct {
|
||||
IsPatch bool `json:"is-patch"`
|
||||
IsCompatible bool `json:"is-compatible"`
|
||||
SelectedMap map[string]string `json:"selected-map"`
|
||||
TestURL *string `json:"test-url"`
|
||||
}
|
||||
|
||||
type GenerateConfigParams struct {
|
||||
ProfilePath *string `json:"profile-path"`
|
||||
Config *config.RawConfig `json:"config" `
|
||||
IsPatch *bool `json:"is-patch"`
|
||||
IsCompatible *bool `json:"is-compatible"`
|
||||
ProfilePath *string `json:"profile-path"`
|
||||
Config config.RawConfig `json:"config" `
|
||||
Params ConfigExtendedParams `json:"params"`
|
||||
}
|
||||
|
||||
type ChangeProxyParams struct {
|
||||
@@ -170,9 +177,9 @@ func getRawConfigWithPath(path *string) *config.RawConfig {
|
||||
}
|
||||
}
|
||||
|
||||
func decorationConfig(profilePath *string, cfg config.RawConfig, compatible bool) *config.RawConfig {
|
||||
func decorationConfig(profilePath *string, cfg config.RawConfig) *config.RawConfig {
|
||||
prof := getRawConfigWithPath(profilePath)
|
||||
overwriteConfig(prof, cfg, compatible)
|
||||
overwriteConfig(prof, cfg)
|
||||
return prof
|
||||
}
|
||||
|
||||
@@ -322,7 +329,7 @@ func generateProxyGroupAndRule(proxyGroup *[]map[string]any, rule *[]string) {
|
||||
*rule = computedRule
|
||||
}
|
||||
|
||||
func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig, compatible bool) {
|
||||
func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig) {
|
||||
targetConfig.ExternalController = patchConfig.ExternalController
|
||||
targetConfig.ExternalUI = ""
|
||||
targetConfig.Interface = ""
|
||||
@@ -340,8 +347,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 {
|
||||
@@ -352,7 +359,7 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
|
||||
//} else if runtime.GOOS == "windows" {
|
||||
// targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, dns.SystemDNSPlaceholder)
|
||||
//}
|
||||
if compatible == false {
|
||||
if configParams.IsCompatible == false {
|
||||
targetConfig.ProxyProvider = make(map[string]map[string]any)
|
||||
targetConfig.RuleProvider = make(map[string]map[string]any)
|
||||
generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule)
|
||||
@@ -388,16 +395,44 @@ func patchConfig(general *config.General) {
|
||||
resolver.DisableIPv6 = !general.IPv6
|
||||
}
|
||||
|
||||
func applyConfig(isPatch bool) {
|
||||
func patchSelectGroup() {
|
||||
mapping := configParams.SelectedMap
|
||||
if mapping == nil {
|
||||
return
|
||||
}
|
||||
for name, proxy := range tunnel.ProxiesWithProviders() {
|
||||
outbound, ok := proxy.(*adapter.Proxy)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
selector, ok := outbound.ProxyAdapter.(outboundgroup.SelectAble)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
selected, exist := mapping[name]
|
||||
if !exist {
|
||||
continue
|
||||
}
|
||||
|
||||
selector.ForceSet(selected)
|
||||
}
|
||||
}
|
||||
|
||||
func applyConfig() {
|
||||
cfg, err := config.ParseRawConfig(currentConfig)
|
||||
if err != nil {
|
||||
cfg, _ = config.ParseRawConfig(config.DefaultRawConfig())
|
||||
}
|
||||
if isPatch {
|
||||
if configParams.TestURL != nil {
|
||||
constant.DefaultTestURL = *configParams.TestURL
|
||||
}
|
||||
if configParams.IsPatch {
|
||||
patchConfig(cfg.General)
|
||||
} else {
|
||||
executor.Shutdown()
|
||||
runtime.GC()
|
||||
hub.UltraApplyConfig(cfg, true)
|
||||
patchSelectGroup()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ const (
|
||||
Process MessageType = "process"
|
||||
Request MessageType = "request"
|
||||
Run MessageType = "run"
|
||||
Loaded MessageType = "loaded"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
|
||||
35
core/hub.go
35
core/hub.go
@@ -31,6 +31,8 @@ import (
|
||||
|
||||
var currentConfig = config.DefaultRawConfig()
|
||||
|
||||
var configParams = ConfigExtendedParams{}
|
||||
|
||||
var isInit = false
|
||||
|
||||
//export initClash
|
||||
@@ -92,17 +94,14 @@ func updateConfig(s *C.char, port C.longlong) {
|
||||
go func() {
|
||||
var params = &GenerateConfigParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), params)
|
||||
configParams = params.Params
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
prof := decorationConfig(params.ProfilePath, *params.Config, *params.IsCompatible)
|
||||
prof := decorationConfig(params.ProfilePath, params.Config)
|
||||
currentConfig = prof
|
||||
if *params.IsPatch {
|
||||
applyConfig(true)
|
||||
} else {
|
||||
applyConfig(false)
|
||||
}
|
||||
applyConfig()
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
@@ -150,7 +149,7 @@ func getProxies() *C.char {
|
||||
}
|
||||
|
||||
//export changeProxy
|
||||
func changeProxy(s *C.char) bool {
|
||||
func changeProxy(s *C.char) {
|
||||
paramsString := C.GoString(s)
|
||||
go func() {
|
||||
var params = &ChangeProxyParams{}
|
||||
@@ -158,22 +157,24 @@ func changeProxy(s *C.char) bool {
|
||||
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, ok := proxies[groupName]
|
||||
if !ok {
|
||||
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
|
||||
@@ -439,4 +440,10 @@ func init() {
|
||||
Data: c,
|
||||
})
|
||||
}
|
||||
executor.DefaultProxyProviderLoadedHook = func(providerName string) {
|
||||
bridge.SendMessage(bridge.Message{
|
||||
Type: bridge.Loaded,
|
||||
Data: providerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
52
core/tun.go
52
core/tun.go
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"golang.org/x/sync/semaphore"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
@@ -18,6 +19,21 @@ import (
|
||||
var tunLock sync.Mutex
|
||||
var tun *t.Tun
|
||||
|
||||
type FdMap struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (cm *FdMap) Store(key int64) {
|
||||
cm.m.Store(key, struct{}{})
|
||||
}
|
||||
|
||||
func (cm *FdMap) Load(key int64) bool {
|
||||
_, ok := cm.m.Load(key)
|
||||
return ok
|
||||
}
|
||||
|
||||
var fdMap FdMap
|
||||
|
||||
//export startTUN
|
||||
func startTUN(fd C.int) {
|
||||
go func() {
|
||||
@@ -63,15 +79,45 @@ func stopTun() {
|
||||
|
||||
var errBlocked = errors.New("blocked")
|
||||
|
||||
//export setFdMap
|
||||
func setFdMap(fd C.long) {
|
||||
fdInt := int64(fd)
|
||||
go func() {
|
||||
fdMap.Store(fdInt)
|
||||
}()
|
||||
}
|
||||
|
||||
var fdCounter int64 = 0
|
||||
|
||||
func init() {
|
||||
dialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error {
|
||||
if platform.ShouldBlockConnection() {
|
||||
return errBlocked
|
||||
}
|
||||
return conn.Control(func(fd uintptr) {
|
||||
if tun != nil {
|
||||
tun.MarkSocket(int(fd))
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
if tun == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fdInt := int64(fd)
|
||||
timeout := time.After(100 * time.Millisecond)
|
||||
id := atomic.AddInt64(&fdCounter, 1)
|
||||
tun.MarkSocket(t.Fd{
|
||||
Id: id,
|
||||
Value: fdInt,
|
||||
})
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return
|
||||
default:
|
||||
exists := fdMap.Load(id)
|
||||
if exists {
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -187,7 +186,12 @@ func Start(fd int, gateway, portal, dns string) (io.Closer, error) {
|
||||
return stack, nil
|
||||
}
|
||||
|
||||
func (t *Tun) MarkSocket(fd int) {
|
||||
type Fd struct {
|
||||
Id int64 `json:"id"`
|
||||
Value int64 `json:"value"`
|
||||
}
|
||||
|
||||
func (t *Tun) MarkSocket(fd Fd) {
|
||||
_ = t.Limit.Acquire(context.Background(), 1)
|
||||
defer t.Limit.Release(1)
|
||||
|
||||
@@ -197,7 +201,7 @@ func (t *Tun) MarkSocket(fd int) {
|
||||
|
||||
message := &bridge.Message{
|
||||
Type: bridge.Tun,
|
||||
Data: strconv.Itoa(fd),
|
||||
Data: fd,
|
||||
}
|
||||
|
||||
bridge.SendMessage(*message)
|
||||
|
||||
@@ -117,6 +117,7 @@ class ClashCore {
|
||||
.map(
|
||||
(name) => proxies[name],
|
||||
)
|
||||
.where((proxy) => proxy != null)
|
||||
.toList();
|
||||
return group;
|
||||
}).toList();
|
||||
@@ -164,12 +165,11 @@ class ClashCore {
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
bool changeProxy(ChangeProxyParams changeProxyParams) {
|
||||
changeProxy(ChangeProxyParams changeProxyParams) {
|
||||
final params = json.encode(changeProxyParams);
|
||||
final paramsChar = params.toNativeUtf8().cast<Char>();
|
||||
final isInit = clashFFI.changeProxy(paramsChar) == 1;
|
||||
clashFFI.changeProxy(paramsChar);
|
||||
malloc.free(paramsChar);
|
||||
return isInit;
|
||||
}
|
||||
|
||||
Future<Delay> getDelay(String proxyName) {
|
||||
@@ -185,7 +185,8 @@ class ClashCore {
|
||||
receiver.close();
|
||||
}
|
||||
});
|
||||
final delayParamsChar = json.encode(delayParams).toNativeUtf8().cast<Char>();
|
||||
final delayParamsChar =
|
||||
json.encode(delayParams).toNativeUtf8().cast<Char>();
|
||||
clashFFI.asyncTestDelay(
|
||||
delayParamsChar,
|
||||
receiver.sendPort.nativePort,
|
||||
@@ -254,11 +255,16 @@ class ClashCore {
|
||||
}
|
||||
|
||||
void setProcessMap(ProcessMapItem processMapItem) {
|
||||
final processMapItemChar = json.encode(processMapItem).toNativeUtf8().cast<Char>();
|
||||
final processMapItemChar =
|
||||
json.encode(processMapItem).toNativeUtf8().cast<Char>();
|
||||
clashFFI.setProcessMap(processMapItemChar);
|
||||
malloc.free(processMapItemChar);
|
||||
}
|
||||
|
||||
void setFdMap(int fd) {
|
||||
clashFFI.setFdMap(fd);
|
||||
}
|
||||
|
||||
// DateTime? getRunTime() {
|
||||
// final runTimeString = clashFFI.getRunTime().cast<Utf8>().toDartString();
|
||||
// if (runTimeString.isEmpty) return null;
|
||||
|
||||
@@ -5248,7 +5248,7 @@ class ClashFFI {
|
||||
late final _getProxies =
|
||||
_getProxiesPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
|
||||
|
||||
int changeProxy(
|
||||
void changeProxy(
|
||||
ffi.Pointer<ffi.Char> s,
|
||||
) {
|
||||
return _changeProxy(
|
||||
@@ -5257,10 +5257,10 @@ class ClashFFI {
|
||||
}
|
||||
|
||||
late final _changeProxyPtr =
|
||||
_lookup<ffi.NativeFunction<GoUint8 Function(ffi.Pointer<ffi.Char>)>>(
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(
|
||||
'changeProxy');
|
||||
late final _changeProxy =
|
||||
_changeProxyPtr.asFunction<int Function(ffi.Pointer<ffi.Char>)>();
|
||||
_changeProxyPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
|
||||
|
||||
ffi.Pointer<ffi.Char> getTraffic() {
|
||||
return _getTraffic();
|
||||
@@ -5484,6 +5484,18 @@ class ClashFFI {
|
||||
late final _stopTunPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function()>>('stopTun');
|
||||
late final _stopTun = _stopTunPtr.asFunction<void Function()>();
|
||||
|
||||
void setFdMap(
|
||||
int fd,
|
||||
) {
|
||||
return _setFdMap(
|
||||
fd,
|
||||
);
|
||||
}
|
||||
|
||||
late final _setFdMapPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Long)>>('setFdMap');
|
||||
late final _setFdMap = _setFdMapPtr.asFunction<void Function(int)>();
|
||||
}
|
||||
|
||||
typedef va_list = ffi.Pointer<ffi.Char>;
|
||||
|
||||
@@ -10,7 +10,7 @@ import 'core.dart';
|
||||
abstract mixin class ClashMessageListener {
|
||||
void onLog(Log log) {}
|
||||
|
||||
void onTun(String fd) {}
|
||||
void onTun(Fd fd) {}
|
||||
|
||||
void onDelay(Delay delay) {}
|
||||
|
||||
@@ -21,6 +21,8 @@ abstract mixin class ClashMessageListener {
|
||||
void onNow(Now now) {}
|
||||
|
||||
void onRun(String runTime) {}
|
||||
|
||||
void onLoaded(String groupName) {}
|
||||
}
|
||||
|
||||
class ClashMessage {
|
||||
@@ -39,7 +41,7 @@ class ClashMessage {
|
||||
listener.onLog(Log.fromJson(m.data));
|
||||
break;
|
||||
case MessageType.tun:
|
||||
listener.onTun(m.data);
|
||||
listener.onTun(Fd.fromJson(m.data));
|
||||
break;
|
||||
case MessageType.delay:
|
||||
listener.onDelay(Delay.fromJson(m.data));
|
||||
@@ -56,6 +58,9 @@ class ClashMessage {
|
||||
case MessageType.run:
|
||||
listener.onRun(m.data);
|
||||
break;
|
||||
case MessageType.loaded:
|
||||
listener.onLoaded(m.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,4 +22,5 @@ export 'string.dart';
|
||||
export 'app_localizations.dart';
|
||||
export 'function.dart';
|
||||
export 'package.dart';
|
||||
export 'measure.dart';
|
||||
export 'measure.dart';
|
||||
export 'service.dart';
|
||||
@@ -24,6 +24,7 @@ const maxMobileWidth = 600;
|
||||
const maxLaptopWidth = 840;
|
||||
const geodataLoaderMemconservative = "memconservative";
|
||||
const geodataLoaderStandard = "standard";
|
||||
const defaultTestUrl = "https://www.gstatic.com/generate_204";
|
||||
final filter = ImageFilter.blur(
|
||||
sigmaX: 5,
|
||||
sigmaY: 5,
|
||||
|
||||
@@ -1,22 +1,13 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
class AppPackage{
|
||||
import 'common.dart';
|
||||
|
||||
static AppPackage? _instance;
|
||||
Completer<PackageInfo> packageInfoCompleter = Completer();
|
||||
|
||||
AppPackage._internal() {
|
||||
PackageInfo.fromPlatform().then(
|
||||
(value) => packageInfoCompleter.complete(value),
|
||||
);
|
||||
}
|
||||
|
||||
factory AppPackage() {
|
||||
_instance ??= AppPackage._internal();
|
||||
return _instance!;
|
||||
}
|
||||
extension PackageInfoExtension on PackageInfo {
|
||||
String get ua => [
|
||||
"$appName/v$version",
|
||||
"clash-verge/v1.6.6",
|
||||
"Platform/${Platform.operatingSystem}",
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
final appPackage = AppPackage();
|
||||
@@ -12,17 +12,18 @@ class Request {
|
||||
bool _isStart = false;
|
||||
|
||||
Request() {
|
||||
_dio = Dio(
|
||||
BaseOptions(
|
||||
headers: {"User-Agent": coreName},
|
||||
_dio = Dio();
|
||||
_dio.options = BaseOptions(
|
||||
headers: {"User-Agent": globalState.appController.clashConfig.globalUa},
|
||||
);
|
||||
_dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
_syncProxy();
|
||||
return handler.next(options); // 继续请求
|
||||
},
|
||||
),
|
||||
);
|
||||
_dio.interceptors.add(InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
_syncProxy();
|
||||
return handler.next(options); // 继续请求
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
_syncProxy() {
|
||||
@@ -68,8 +69,7 @@ class Request {
|
||||
if (response.statusCode != 200) return null;
|
||||
final data = response.data as Map<String, dynamic>;
|
||||
final remoteVersion = data['tag_name'];
|
||||
final packageInfo = await appPackage.packageInfoCompleter.future;
|
||||
final version = packageInfo.version;
|
||||
final version = globalState.packageInfo.version;
|
||||
final hasUpdate =
|
||||
other.compareVersions(remoteVersion.replaceAll('v', ''), version) > 0;
|
||||
if (!hasUpdate) return null;
|
||||
|
||||
110
lib/common/service.dart
Normal file
110
lib/common/service.dart
Normal file
@@ -0,0 +1,110 @@
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:win32/win32.dart';
|
||||
|
||||
typedef CreateServiceNative = IntPtr Function(
|
||||
IntPtr hSCManager,
|
||||
Pointer<Utf16> lpServiceName,
|
||||
Pointer<Utf16> lpDisplayName,
|
||||
Uint32 dwDesiredAccess,
|
||||
Uint32 dwServiceType,
|
||||
Uint32 dwStartType,
|
||||
Uint32 dwErrorControl,
|
||||
Pointer<Utf16> lpBinaryPathName,
|
||||
Pointer<Utf16> lpLoadOrderGroup,
|
||||
Pointer<Uint32> lpdwTagId,
|
||||
Pointer<Utf16> lpDependencies,
|
||||
Pointer<Utf16> lpServiceStartName,
|
||||
Pointer<Utf16> lpPassword,
|
||||
);
|
||||
|
||||
typedef CreateServiceDart = int Function(
|
||||
int hSCManager,
|
||||
Pointer<Utf16> lpServiceName,
|
||||
Pointer<Utf16> lpDisplayName,
|
||||
int dwDesiredAccess,
|
||||
int dwServiceType,
|
||||
int dwStartType,
|
||||
int dwErrorControl,
|
||||
Pointer<Utf16> lpBinaryPathName,
|
||||
Pointer<Utf16> lpLoadOrderGroup,
|
||||
Pointer<Uint32> lpdwTagId,
|
||||
Pointer<Utf16> lpDependencies,
|
||||
Pointer<Utf16> lpServiceStartName,
|
||||
Pointer<Utf16> lpPassword,
|
||||
);
|
||||
|
||||
const _SERVICE_ALL_ACCESS = 0xF003F;
|
||||
|
||||
const _SERVICE_WIN32_OWN_PROCESS = 0x00000010;
|
||||
|
||||
const _SERVICE_AUTO_START = 0x00000002;
|
||||
|
||||
const _SERVICE_ERROR_NORMAL = 0x00000001;
|
||||
|
||||
typedef GetLastErrorNative = Uint32 Function();
|
||||
typedef GetLastErrorDart = int Function();
|
||||
|
||||
class Service {
|
||||
static Service? _instance;
|
||||
late DynamicLibrary _advapi32;
|
||||
|
||||
Service._internal() {
|
||||
_advapi32 = DynamicLibrary.open('advapi32.dll');
|
||||
}
|
||||
|
||||
factory Service() {
|
||||
_instance ??= Service._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<void> createService() async {
|
||||
final int scManager = OpenSCManager(nullptr, nullptr, _SERVICE_ALL_ACCESS);
|
||||
if (scManager == 0) return;
|
||||
final serviceName = 'FlClash Service'.toNativeUtf16();
|
||||
final displayName = 'FlClash Service'.toNativeUtf16();
|
||||
final binaryPathName = "C:\\Application\\Clash.Verge_1.6.6_x64_portable\\resources\\clash-verge-service.exe".toNativeUtf16();
|
||||
final createService =
|
||||
_advapi32.lookupFunction<CreateServiceNative, CreateServiceDart>(
|
||||
'CreateServiceW',
|
||||
);
|
||||
final getLastError = DynamicLibrary.open('kernel32.dll')
|
||||
.lookupFunction<GetLastErrorNative, GetLastErrorDart>('GetLastError');
|
||||
|
||||
final serviceHandle = createService(
|
||||
scManager,
|
||||
serviceName,
|
||||
displayName,
|
||||
_SERVICE_ALL_ACCESS,
|
||||
_SERVICE_WIN32_OWN_PROCESS,
|
||||
_SERVICE_AUTO_START,
|
||||
_SERVICE_ERROR_NORMAL,
|
||||
binaryPathName,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
);
|
||||
|
||||
print("serviceHandle $serviceHandle");
|
||||
|
||||
final errorCode = GetLastError();
|
||||
print('Error code: $errorCode');
|
||||
|
||||
final result = StartService(serviceHandle, 0, nullptr);
|
||||
|
||||
if (result == 0) {
|
||||
print('Failed to start the service.');
|
||||
} else {
|
||||
print('Service started successfully.');
|
||||
}
|
||||
|
||||
calloc.free(serviceName);
|
||||
calloc.free(displayName);
|
||||
calloc.free(binaryPathName);
|
||||
}
|
||||
}
|
||||
|
||||
final service = Platform.isWindows ? Service() : null;
|
||||
@@ -1,10 +1,9 @@
|
||||
extension StringExtension on String {
|
||||
bool get isUrl {
|
||||
try {
|
||||
Uri.parse(this);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return RegExp(
|
||||
r"^(http(s)?://)?(www\.)?[a-zA-Z0-9]+([\-.][a-zA-Z0-9]+)*\.[a-zA-Z]{2,5}(:[0-9]{1,5})?(/.*)?$",
|
||||
caseSensitive: false,
|
||||
multiLine: false,
|
||||
).hasMatch(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -71,14 +71,6 @@ class AppController {
|
||||
);
|
||||
}
|
||||
|
||||
changeProxy() {
|
||||
globalState.changeProxy(
|
||||
appState: appState,
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
}
|
||||
|
||||
addProfile(Profile profile) async {
|
||||
config.setProfile(profile);
|
||||
if (config.currentProfileId != null) return;
|
||||
|
||||
@@ -56,7 +56,16 @@ enum ProfileType { file, url }
|
||||
|
||||
enum ResultType { success, error }
|
||||
|
||||
enum MessageType { log, tun, delay, process, now, request, run }
|
||||
enum MessageType {
|
||||
log,
|
||||
tun,
|
||||
delay,
|
||||
process,
|
||||
now,
|
||||
request,
|
||||
run,
|
||||
loaded,
|
||||
}
|
||||
|
||||
enum FindProcessMode { always, off }
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class AboutFragment extends StatelessWidget {
|
||||
@@ -49,16 +48,9 @@ class AboutFragment extends StatelessWidget {
|
||||
appName,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
FutureBuilder<PackageInfo>(
|
||||
future: appPackage.packageInfoCompleter.future,
|
||||
builder: (_, package) {
|
||||
final version = package.data?.version;
|
||||
if (version == null) return Container();
|
||||
return Text(
|
||||
version,
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
);
|
||||
},
|
||||
Text(
|
||||
globalState.packageInfo.version,
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
@@ -39,6 +39,104 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
}
|
||||
}
|
||||
|
||||
_showLogLevelDialog(LogLevel value) {
|
||||
globalState.showCommonDialog(
|
||||
child: AlertDialog(
|
||||
title: Text(appLocalizations.logLevel),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 16,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: 250,
|
||||
child: Wrap(
|
||||
children: [
|
||||
for (final logLevel in LogLevel.values)
|
||||
ListItem.radio(
|
||||
delegate: RadioDelegate<LogLevel>(
|
||||
value: logLevel,
|
||||
groupValue: value,
|
||||
onChanged: (LogLevel? value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
final appController = globalState.appController;
|
||||
appController.clashConfig.logLevel = value;
|
||||
appController.updateClashConfigDebounce();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
title: Text(logLevel.name),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_showUaDialog(String? value) {
|
||||
const uas = [
|
||||
null,
|
||||
"clash-verge/v1.6.6",
|
||||
"ClashforWindows/0.19.23",
|
||||
];
|
||||
globalState.showCommonDialog(
|
||||
child: AlertDialog(
|
||||
title: const Text("UA"),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 16,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: 250,
|
||||
child: Wrap(
|
||||
children: [
|
||||
for (final ua in uas)
|
||||
ListItem.radio(
|
||||
delegate: RadioDelegate<String?>(
|
||||
value: ua,
|
||||
groupValue: value,
|
||||
onChanged: (String? value) {
|
||||
final appController = globalState.appController;
|
||||
appController.clashConfig.globalRealUa = value;
|
||||
appController.updateClashConfigDebounce();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
title: Text(ua ?? appLocalizations.defaultText),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_modifyTestUrl(String testUrl) async {
|
||||
final newTestUrl = await globalState.showCommonDialog<String>(
|
||||
child: TestUrlFormDialog(
|
||||
testUrl: testUrl,
|
||||
),
|
||||
);
|
||||
if (newTestUrl != null && newTestUrl != testUrl && mounted) {
|
||||
try {
|
||||
if (!newTestUrl.isUrl) {
|
||||
throw "Invalid url";
|
||||
}
|
||||
globalState.appController.config.testUrl = newTestUrl;
|
||||
globalState.appController.updateClashConfigDebounce();
|
||||
} catch (e) {
|
||||
globalState.showMessage(
|
||||
title: appLocalizations.testUrl,
|
||||
message: TextSpan(
|
||||
text: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildAppSection() {
|
||||
final items = [
|
||||
if (Platform.isAndroid)
|
||||
@@ -89,9 +187,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
onChanged: (bool value) async {
|
||||
final appController = globalState.appController;
|
||||
appController.config.isCompatible = value;
|
||||
await appController.updateClashConfig(isPatch: false);
|
||||
await appController.updateGroups();
|
||||
appController.changeProxy();
|
||||
await appController.applyProfile();
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -114,42 +210,6 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
);
|
||||
}
|
||||
|
||||
_showLogLevelDialog(LogLevel value) {
|
||||
globalState.showCommonDialog(
|
||||
child: AlertDialog(
|
||||
title: Text(appLocalizations.logLevel),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 16,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: 250,
|
||||
child: Wrap(
|
||||
children: [
|
||||
for (final logLevel in LogLevel.values)
|
||||
ListItem.radio(
|
||||
delegate: RadioDelegate<LogLevel>(
|
||||
value: logLevel,
|
||||
groupValue: value,
|
||||
onChanged: (LogLevel? value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
final appController = globalState.appController;
|
||||
appController.clashConfig.logLevel = value;
|
||||
appController.updateClashConfigDebounce();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
title: Text(logLevel.name),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGeneralSection() {
|
||||
final items = [
|
||||
Selector<ClashConfig, LogLevel>(
|
||||
@@ -165,6 +225,32 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<ClashConfig, String?>(
|
||||
selector: (_, clashConfig) => clashConfig.globalRealUa,
|
||||
builder: (_, value, __) {
|
||||
return ListItem(
|
||||
leading: const Icon(Icons.computer_outlined),
|
||||
title: const Text("UA"),
|
||||
subtitle: Text(value ?? appLocalizations.defaultText),
|
||||
onTab: () {
|
||||
_showUaDialog(value);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<Config, String>(
|
||||
selector: (_, config) => config.testUrl,
|
||||
builder: (_, value, __) {
|
||||
return ListItem(
|
||||
leading: const Icon(Icons.timeline),
|
||||
title: Text(appLocalizations.testUrl),
|
||||
subtitle: Text(value),
|
||||
onTab: () {
|
||||
_modifyTestUrl(value);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<ClashConfig, int>(
|
||||
selector: (_, clashConfig) => clashConfig.mixedPort,
|
||||
builder: (_, mixedPort, __) {
|
||||
@@ -337,14 +423,12 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
|
||||
Widget _buildMoreSection() {
|
||||
final items = [
|
||||
if (false)
|
||||
if (system.isDesktop)
|
||||
Selector<ClashConfig, bool>(
|
||||
selector: (_, clashConfig) => clashConfig.tun.enable,
|
||||
builder: (_, tunEnable, __) {
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(
|
||||
Icons.important_devices_outlined
|
||||
),
|
||||
leading: const Icon(Icons.important_devices_outlined),
|
||||
title: Text(appLocalizations.tun),
|
||||
subtitle: Text(appLocalizations.tunDesc),
|
||||
delegate: SwitchDelegate(
|
||||
@@ -359,9 +443,9 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
},
|
||||
),
|
||||
];
|
||||
if(items.isEmpty) return Container();
|
||||
if (items.isEmpty) return Container();
|
||||
return Section(
|
||||
title: appLocalizations.general,
|
||||
title: appLocalizations.more,
|
||||
child: Column(
|
||||
children: [
|
||||
for (final item in items) ...[
|
||||
@@ -414,7 +498,7 @@ class _MixedPortFormDialogState extends State<MixedPortFormDialog> {
|
||||
portController = TextEditingController(text: "${widget.mixedPort}");
|
||||
}
|
||||
|
||||
_handleAddProfileFormURL() async {
|
||||
_handleUpdate() async {
|
||||
final port = portController.value.text;
|
||||
if (port.isEmpty) return;
|
||||
Navigator.of(context).pop<String>(port);
|
||||
@@ -440,7 +524,64 @@ class _MixedPortFormDialogState extends State<MixedPortFormDialog> {
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _handleAddProfileFormURL,
|
||||
onPressed: _handleUpdate,
|
||||
child: Text(appLocalizations.submit),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TestUrlFormDialog extends StatefulWidget {
|
||||
final String testUrl;
|
||||
|
||||
const TestUrlFormDialog({
|
||||
super.key,
|
||||
required this.testUrl,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TestUrlFormDialog> createState() => _TestUrlFormDialogState();
|
||||
}
|
||||
|
||||
class _TestUrlFormDialogState extends State<TestUrlFormDialog> {
|
||||
late TextEditingController testUrlController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
testUrlController = TextEditingController(text: widget.testUrl);
|
||||
}
|
||||
|
||||
_handleUpdate() async {
|
||||
final testUrl = testUrlController.value.text;
|
||||
if (testUrl.isEmpty) return;
|
||||
Navigator.of(context).pop<String>(testUrl);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(appLocalizations.testUrl),
|
||||
content: SizedBox(
|
||||
width: 300,
|
||||
child: Wrap(
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
TextField(
|
||||
maxLines: 5,
|
||||
minLines: 1,
|
||||
controller: testUrlController,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _handleUpdate,
|
||||
child: Text(appLocalizations.submit),
|
||||
)
|
||||
],
|
||||
|
||||
@@ -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, __) {
|
||||
|
||||
@@ -52,6 +52,7 @@ class _NetworkDetectionState extends State<NetworkDetection> {
|
||||
isInit: appState.isInit,
|
||||
selectedMap: appState.selectedMap,
|
||||
isStart: appState.isStart,
|
||||
checkIpNum: appState.checkIpNum,
|
||||
);
|
||||
},
|
||||
builder: (_, state, __) {
|
||||
@@ -137,7 +138,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(
|
||||
|
||||
@@ -13,18 +13,9 @@ class OutboundMode extends StatelessWidget {
|
||||
_changeMode(BuildContext context, Mode? value) async {
|
||||
final appController = globalState.appController;
|
||||
final clashConfig = appController.clashConfig;
|
||||
final config = appController.config;
|
||||
if (value == null || clashConfig.mode == value) return;
|
||||
clashConfig.mode = value;
|
||||
await appController.updateClashConfig();
|
||||
if (!config.isCompatible) {
|
||||
final proxySelected = config.currentSelectedMap[GroupName.Proxy.name];
|
||||
final globalSelected = config.currentSelectedMap[GroupName.GLOBAL.name];
|
||||
if (proxySelected != null && globalSelected == null) {
|
||||
config.updateCurrentSelectedMap(GroupName.GLOBAL.name, proxySelected);
|
||||
}
|
||||
}
|
||||
appController.changeProxy();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -64,11 +55,8 @@ class OutboundMode extends StatelessWidget {
|
||||
),
|
||||
title: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme
|
||||
.of(context)
|
||||
.textTheme
|
||||
.titleMedium
|
||||
?.toSoftBold,
|
||||
style:
|
||||
Theme.of(context).textTheme.titleMedium?.toSoftBold,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -831,7 +831,12 @@ class ProxyCard extends StatelessWidget {
|
||||
groupName,
|
||||
proxy.name,
|
||||
);
|
||||
appController.changeProxy();
|
||||
clashCore.changeProxy(
|
||||
ChangeProxyParams(
|
||||
groupName: groupName,
|
||||
proxyName: proxy.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"overrideDesc": "Override Proxy related config",
|
||||
"allowLan": "AllowLan",
|
||||
"allowLanDesc": "Allow access proxy through the LAN",
|
||||
"tun": "Tun mode",
|
||||
"tun": "TUN mode",
|
||||
"tunDesc": "only effective in administrator mode",
|
||||
"minimizeOnExit": "Minimize on exit",
|
||||
"minimizeOnExitDesc": "Modify the default system exit event",
|
||||
@@ -191,5 +191,6 @@
|
||||
"view": "View",
|
||||
"cut": "Cut",
|
||||
"copy": "Copy",
|
||||
"paste": "Paste"
|
||||
"paste": "Paste",
|
||||
"testUrl": "Test url"
|
||||
}
|
||||
@@ -37,7 +37,7 @@
|
||||
"overrideDesc": "覆写代理相关配置",
|
||||
"allowLan": "局域网代理",
|
||||
"allowLanDesc": "允许通过局域网访问代理",
|
||||
"tun": "Tun模式",
|
||||
"tun": "TUN模式",
|
||||
"tunDesc": "仅在管理员模式生效",
|
||||
"minimizeOnExit": "退出时最小化",
|
||||
"minimizeOnExitDesc": "修改系统默认退出事件",
|
||||
@@ -191,5 +191,6 @@
|
||||
"view": "查看",
|
||||
"cut": "剪切",
|
||||
"copy": "复制",
|
||||
"paste": "粘贴"
|
||||
"paste": "粘贴",
|
||||
"testUrl": "测速链接"
|
||||
}
|
||||
@@ -269,6 +269,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP concurrent"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Enabling it will allow TCP concurrency"),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("Test url"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("Theme"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("Theme color"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -277,7 +278,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tip": MessageLookupByLibrary.simpleMessage("tip"),
|
||||
"tools": MessageLookupByLibrary.simpleMessage("Tools"),
|
||||
"trafficUsage": MessageLookupByLibrary.simpleMessage("Traffic usage"),
|
||||
"tun": MessageLookupByLibrary.simpleMessage("Tun mode"),
|
||||
"tun": MessageLookupByLibrary.simpleMessage("TUN mode"),
|
||||
"tunDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"only effective in administrator mode"),
|
||||
"unableToUpdateCurrentProfileDesc":
|
||||
|
||||
@@ -217,6 +217,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("开启后,主页选项卡将添加切换动画"),
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("测速链接"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("主题"),
|
||||
"themeColor": MessageLookupByLibrary.simpleMessage("主题色彩"),
|
||||
"themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"),
|
||||
@@ -224,7 +225,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tip": MessageLookupByLibrary.simpleMessage("提示"),
|
||||
"tools": MessageLookupByLibrary.simpleMessage("工具"),
|
||||
"trafficUsage": MessageLookupByLibrary.simpleMessage("流量统计"),
|
||||
"tun": MessageLookupByLibrary.simpleMessage("Tun模式"),
|
||||
"tun": MessageLookupByLibrary.simpleMessage("TUN模式"),
|
||||
"tunDesc": MessageLookupByLibrary.simpleMessage("仅在管理员模式生效"),
|
||||
"unableToUpdateCurrentProfileDesc":
|
||||
MessageLookupByLibrary.simpleMessage("无法更新当前配置文件"),
|
||||
|
||||
@@ -430,10 +430,10 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Tun mode`
|
||||
/// `TUN mode`
|
||||
String get tun {
|
||||
return Intl.message(
|
||||
'Tun mode',
|
||||
'TUN mode',
|
||||
name: 'tun',
|
||||
desc: '',
|
||||
args: [],
|
||||
@@ -1979,6 +1979,16 @@ class AppLocalizations {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Test url`
|
||||
String get testUrl {
|
||||
return Intl.message(
|
||||
'Test url',
|
||||
name: 'testUrl',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/plugins/tile.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'application.dart';
|
||||
import 'l10n/l10n.dart';
|
||||
import 'models/models.dart';
|
||||
@@ -15,6 +16,7 @@ Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await android?.init();
|
||||
await window?.init();
|
||||
globalState.packageInfo = await PackageInfo.fromPlatform();
|
||||
final config = await preferences.getConfig() ?? Config();
|
||||
final clashConfig = await preferences.getClashConfig() ?? ClashConfig();
|
||||
final appState = AppState(
|
||||
@@ -49,11 +51,25 @@ 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: (Fd fd) async {
|
||||
await proxyManager.setProtect(fd.value);
|
||||
clashCore.setFdMap(fd.id);
|
||||
},
|
||||
onLoaded: (String groupName) {
|
||||
final currentSelectedMap = config.currentSelectedMap;
|
||||
final proxyName = currentSelectedMap[groupName];
|
||||
if (proxyName == null) return;
|
||||
clashCore.changeProxy(
|
||||
ChangeProxyParams(
|
||||
groupName: groupName,
|
||||
proxyName: proxyName,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await globalState.init(
|
||||
appState: appState,
|
||||
@@ -102,16 +118,24 @@ Future<void> vpnService() async {
|
||||
}
|
||||
|
||||
class ClashMessageListenerWithVpn with ClashMessageListener {
|
||||
final Function(String fd) _onTun;
|
||||
final Function(Fd fd) _onTun;
|
||||
final Function(String) _onLoaded;
|
||||
|
||||
ClashMessageListenerWithVpn({
|
||||
required Function(String fd) onTun,
|
||||
}) : _onTun = onTun;
|
||||
required Function(Fd fd) onTun,
|
||||
required Function(String) onLoaded,
|
||||
}) : _onTun = onTun,
|
||||
_onLoaded = onLoaded;
|
||||
|
||||
@override
|
||||
void onTun(String fd) {
|
||||
void onTun(Fd fd) {
|
||||
_onTun(fd);
|
||||
}
|
||||
|
||||
@override
|
||||
void onLoaded(String groupName) {
|
||||
_onLoaded(groupName);
|
||||
}
|
||||
}
|
||||
|
||||
class TileListenerWithVpn with TileListener {
|
||||
|
||||
@@ -34,6 +34,7 @@ class AppState with ChangeNotifier {
|
||||
List<Group> _groups;
|
||||
double _viewWidth;
|
||||
List<Connection> _requests;
|
||||
num _checkIpNum;
|
||||
|
||||
AppState({
|
||||
required Mode mode,
|
||||
@@ -47,6 +48,7 @@ class AppState with ChangeNotifier {
|
||||
_viewWidth = 0,
|
||||
_selectedMap = selectedMap,
|
||||
_sortNum = 0,
|
||||
_checkIpNum = 0,
|
||||
_requests = [],
|
||||
_mode = mode,
|
||||
_totalTraffic = Traffic(),
|
||||
@@ -123,9 +125,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 {
|
||||
@@ -240,6 +243,15 @@ class AppState with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
num get checkIpNum => _checkIpNum;
|
||||
|
||||
set checkIpNum(num value) {
|
||||
if (_checkIpNum != value) {
|
||||
_checkIpNum = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Mode get mode => _mode;
|
||||
|
||||
set mode(Mode value) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// 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:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -119,6 +121,7 @@ class ClashConfig extends ChangeNotifier {
|
||||
Tun _tun;
|
||||
Dns _dns;
|
||||
List<String> _rules;
|
||||
String? _globalRealUa;
|
||||
|
||||
ClashConfig()
|
||||
: _mixedPort = 7890,
|
||||
@@ -235,7 +238,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) {
|
||||
@@ -262,6 +270,25 @@ class ClashConfig extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonKey(name: "global-ua", defaultValue: null)
|
||||
String get globalUa {
|
||||
if (_globalRealUa == null) {
|
||||
return globalState.packageInfo.ua;
|
||||
} else {
|
||||
return _globalRealUa!;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonKey(name: "global-real-ua", defaultValue: null)
|
||||
String? get globalRealUa => _globalRealUa;
|
||||
|
||||
set globalRealUa(String? value) {
|
||||
if (_globalRealUa != value) {
|
||||
_globalRealUa = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
update([ClashConfig? clashConfig]) {
|
||||
if (clashConfig != null) {
|
||||
_mixedPort = clashConfig._mixedPort;
|
||||
@@ -271,6 +298,7 @@ class ClashConfig extends ChangeNotifier {
|
||||
_tun = clashConfig._tun;
|
||||
_dns = clashConfig._dns;
|
||||
_rules = clashConfig._rules;
|
||||
_globalRealUa = clashConfig.globalRealUa;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -282,9 +310,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}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ class Config extends ChangeNotifier {
|
||||
ProxiesType _proxiesType;
|
||||
ProxyCardType _proxyCardType;
|
||||
int _proxiesColumns;
|
||||
String _testUrl;
|
||||
|
||||
Config()
|
||||
: _profiles = [],
|
||||
@@ -75,6 +76,7 @@ class Config extends ChangeNotifier {
|
||||
_isAccessControl = false,
|
||||
_autoCheckUpdate = true,
|
||||
_systemProxy = true,
|
||||
_testUrl = defaultTestUrl,
|
||||
_accessControl = const AccessControl(),
|
||||
_isAnimateToPage = true,
|
||||
_allowBypass = true,
|
||||
@@ -414,6 +416,17 @@ class Config extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonKey(name: "test-url", defaultValue: defaultTestUrl)
|
||||
String get testUrl => _testUrl;
|
||||
|
||||
set testUrl(String value) {
|
||||
if (_testUrl != value) {
|
||||
_testUrl = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
update([
|
||||
Config? config,
|
||||
RecoveryOption recoveryOptions = RecoveryOption.all,
|
||||
@@ -446,6 +459,7 @@ class Config extends ChangeNotifier {
|
||||
_isAnimateToPage = config._isAnimateToPage;
|
||||
_autoCheckUpdate = config._autoCheckUpdate;
|
||||
_dav = config._dav;
|
||||
_testUrl = config.testUrl;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -3,19 +3,32 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/clash_config.dart';
|
||||
import 'package:fl_clash/models/connection.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'generated/ffi.g.dart';
|
||||
|
||||
part 'generated/ffi.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class ConfigExtendedParams with _$ConfigExtendedParams {
|
||||
const factory ConfigExtendedParams({
|
||||
@JsonKey(name: "is-patch") required bool isPatch,
|
||||
@JsonKey(name: "is-compatible") required bool isCompatible,
|
||||
@JsonKey(name: "selected-map") required SelectedMap selectedMap,
|
||||
@JsonKey(name: "test-url") required String testUrl,
|
||||
}) = _ConfigExtendedParams;
|
||||
|
||||
factory ConfigExtendedParams.fromJson(Map<String, Object?> json) =>
|
||||
_$ConfigExtendedParamsFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class UpdateConfigParams with _$UpdateConfigParams {
|
||||
const factory UpdateConfigParams({
|
||||
@JsonKey(name: "profile-path") String? profilePath,
|
||||
required ClashConfig config,
|
||||
@JsonKey(name: "is-patch") required bool isPatch,
|
||||
@JsonKey(name: "is-compatible") required bool isCompatible,
|
||||
required ConfigExtendedParams params,
|
||||
}) = _UpdateConfigParams;
|
||||
|
||||
factory UpdateConfigParams.fromJson(Map<String, Object?> json) =>
|
||||
@@ -75,6 +88,16 @@ class Process with _$Process {
|
||||
_$ProcessFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Fd with _$Fd {
|
||||
const factory Fd({
|
||||
required int id,
|
||||
required int value,
|
||||
}) = _Fd;
|
||||
|
||||
factory Fd.fromJson(Map<String, Object?> json) => _$FdFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProcessMapItem with _$ProcessMapItem {
|
||||
const factory ProcessMapItem({
|
||||
|
||||
@@ -51,7 +51,8 @@ ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => ClashConfig()
|
||||
..tcpConcurrent = json['tcp-concurrent'] as bool? ?? false
|
||||
..tun = Tun.fromJson(json['tun'] as Map<String, dynamic>)
|
||||
..dns = Dns.fromJson(json['dns'] as Map<String, dynamic>)
|
||||
..rules = (json['rules'] as List<dynamic>).map((e) => e as String).toList();
|
||||
..rules = (json['rules'] as List<dynamic>).map((e) => e as String).toList()
|
||||
..globalRealUa = json['global-real-ua'] as String?;
|
||||
|
||||
Map<String, dynamic> _$ClashConfigToJson(ClashConfig instance) =>
|
||||
<String, dynamic>{
|
||||
@@ -68,6 +69,7 @@ Map<String, dynamic> _$ClashConfigToJson(ClashConfig instance) =>
|
||||
'tun': instance.tun,
|
||||
'dns': instance.dns,
|
||||
'rules': instance.rules,
|
||||
'global-real-ua': instance.globalRealUa,
|
||||
};
|
||||
|
||||
const _$ModeEnumMap = {
|
||||
|
||||
@@ -41,7 +41,9 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
|
||||
..proxyCardType =
|
||||
$enumDecodeNullable(_$ProxyCardTypeEnumMap, json['proxyCardType']) ??
|
||||
ProxyCardType.expand
|
||||
..proxiesColumns = (json['proxiesColumns'] as num?)?.toInt() ?? 2;
|
||||
..proxiesColumns = (json['proxiesColumns'] as num?)?.toInt() ?? 2
|
||||
..testUrl =
|
||||
json['test-url'] as String? ?? 'https://www.gstatic.com/generate_204';
|
||||
|
||||
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
|
||||
'profiles': instance.profiles,
|
||||
@@ -66,6 +68,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
|
||||
'proxiesType': _$ProxiesTypeEnumMap[instance.proxiesType]!,
|
||||
'proxyCardType': _$ProxyCardTypeEnumMap[instance.proxyCardType]!,
|
||||
'proxiesColumns': instance.proxiesColumns,
|
||||
'test-url': instance.testUrl,
|
||||
};
|
||||
|
||||
const _$ThemeModeEnumMap = {
|
||||
|
||||
@@ -14,6 +14,234 @@ T _$identity<T>(T value) => value;
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
ConfigExtendedParams _$ConfigExtendedParamsFromJson(Map<String, dynamic> json) {
|
||||
return _ConfigExtendedParams.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ConfigExtendedParams {
|
||||
@JsonKey(name: "is-patch")
|
||||
bool get isPatch => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "is-compatible")
|
||||
bool get isCompatible => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "selected-map")
|
||||
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "test-url")
|
||||
String get testUrl => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$ConfigExtendedParamsCopyWith<ConfigExtendedParams> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ConfigExtendedParamsCopyWith<$Res> {
|
||||
factory $ConfigExtendedParamsCopyWith(ConfigExtendedParams value,
|
||||
$Res Function(ConfigExtendedParams) then) =
|
||||
_$ConfigExtendedParamsCopyWithImpl<$Res, ConfigExtendedParams>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "is-patch") bool isPatch,
|
||||
@JsonKey(name: "is-compatible") bool isCompatible,
|
||||
@JsonKey(name: "selected-map") Map<String, String> selectedMap,
|
||||
@JsonKey(name: "test-url") String testUrl});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ConfigExtendedParamsCopyWithImpl<$Res,
|
||||
$Val extends ConfigExtendedParams>
|
||||
implements $ConfigExtendedParamsCopyWith<$Res> {
|
||||
_$ConfigExtendedParamsCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? isPatch = null,
|
||||
Object? isCompatible = null,
|
||||
Object? selectedMap = null,
|
||||
Object? testUrl = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isPatch: null == isPatch
|
||||
? _value.isPatch
|
||||
: isPatch // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isCompatible: null == isCompatible
|
||||
? _value.isCompatible
|
||||
: isCompatible // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
selectedMap: null == selectedMap
|
||||
? _value.selectedMap
|
||||
: selectedMap // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, String>,
|
||||
testUrl: null == testUrl
|
||||
? _value.testUrl
|
||||
: testUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ConfigExtendedParamsImplCopyWith<$Res>
|
||||
implements $ConfigExtendedParamsCopyWith<$Res> {
|
||||
factory _$$ConfigExtendedParamsImplCopyWith(_$ConfigExtendedParamsImpl value,
|
||||
$Res Function(_$ConfigExtendedParamsImpl) then) =
|
||||
__$$ConfigExtendedParamsImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "is-patch") bool isPatch,
|
||||
@JsonKey(name: "is-compatible") bool isCompatible,
|
||||
@JsonKey(name: "selected-map") Map<String, String> selectedMap,
|
||||
@JsonKey(name: "test-url") String testUrl});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ConfigExtendedParamsImplCopyWithImpl<$Res>
|
||||
extends _$ConfigExtendedParamsCopyWithImpl<$Res, _$ConfigExtendedParamsImpl>
|
||||
implements _$$ConfigExtendedParamsImplCopyWith<$Res> {
|
||||
__$$ConfigExtendedParamsImplCopyWithImpl(_$ConfigExtendedParamsImpl _value,
|
||||
$Res Function(_$ConfigExtendedParamsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? isPatch = null,
|
||||
Object? isCompatible = null,
|
||||
Object? selectedMap = null,
|
||||
Object? testUrl = null,
|
||||
}) {
|
||||
return _then(_$ConfigExtendedParamsImpl(
|
||||
isPatch: null == isPatch
|
||||
? _value.isPatch
|
||||
: isPatch // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isCompatible: null == isCompatible
|
||||
? _value.isCompatible
|
||||
: isCompatible // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
selectedMap: null == selectedMap
|
||||
? _value._selectedMap
|
||||
: selectedMap // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, String>,
|
||||
testUrl: null == testUrl
|
||||
? _value.testUrl
|
||||
: testUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
|
||||
const _$ConfigExtendedParamsImpl(
|
||||
{@JsonKey(name: "is-patch") required this.isPatch,
|
||||
@JsonKey(name: "is-compatible") required this.isCompatible,
|
||||
@JsonKey(name: "selected-map")
|
||||
required final Map<String, String> selectedMap,
|
||||
@JsonKey(name: "test-url") required this.testUrl})
|
||||
: _selectedMap = selectedMap;
|
||||
|
||||
factory _$ConfigExtendedParamsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ConfigExtendedParamsImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: "is-patch")
|
||||
final bool isPatch;
|
||||
@override
|
||||
@JsonKey(name: "is-compatible")
|
||||
final bool isCompatible;
|
||||
final Map<String, String> _selectedMap;
|
||||
@override
|
||||
@JsonKey(name: "selected-map")
|
||||
Map<String, String> get selectedMap {
|
||||
if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_selectedMap);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey(name: "test-url")
|
||||
final String testUrl;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ConfigExtendedParams(isPatch: $isPatch, isCompatible: $isCompatible, selectedMap: $selectedMap, testUrl: $testUrl)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ConfigExtendedParamsImpl &&
|
||||
(identical(other.isPatch, isPatch) || other.isPatch == isPatch) &&
|
||||
(identical(other.isCompatible, isCompatible) ||
|
||||
other.isCompatible == isCompatible) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._selectedMap, _selectedMap) &&
|
||||
(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, isPatch, isCompatible,
|
||||
const DeepCollectionEquality().hash(_selectedMap), testUrl);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ConfigExtendedParamsImplCopyWith<_$ConfigExtendedParamsImpl>
|
||||
get copyWith =>
|
||||
__$$ConfigExtendedParamsImplCopyWithImpl<_$ConfigExtendedParamsImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$ConfigExtendedParamsImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ConfigExtendedParams implements ConfigExtendedParams {
|
||||
const factory _ConfigExtendedParams(
|
||||
{@JsonKey(name: "is-patch") required final bool isPatch,
|
||||
@JsonKey(name: "is-compatible") required final bool isCompatible,
|
||||
@JsonKey(name: "selected-map")
|
||||
required final Map<String, String> selectedMap,
|
||||
@JsonKey(name: "test-url") required final String testUrl}) =
|
||||
_$ConfigExtendedParamsImpl;
|
||||
|
||||
factory _ConfigExtendedParams.fromJson(Map<String, dynamic> json) =
|
||||
_$ConfigExtendedParamsImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: "is-patch")
|
||||
bool get isPatch;
|
||||
@override
|
||||
@JsonKey(name: "is-compatible")
|
||||
bool get isCompatible;
|
||||
@override
|
||||
@JsonKey(name: "selected-map")
|
||||
Map<String, String> get selectedMap;
|
||||
@override
|
||||
@JsonKey(name: "test-url")
|
||||
String get testUrl;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$ConfigExtendedParamsImplCopyWith<_$ConfigExtendedParamsImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
UpdateConfigParams _$UpdateConfigParamsFromJson(Map<String, dynamic> json) {
|
||||
return _UpdateConfigParams.fromJson(json);
|
||||
}
|
||||
@@ -23,10 +251,7 @@ mixin _$UpdateConfigParams {
|
||||
@JsonKey(name: "profile-path")
|
||||
String? get profilePath => throw _privateConstructorUsedError;
|
||||
ClashConfig get config => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "is-patch")
|
||||
bool get isPatch => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "is-compatible")
|
||||
bool get isCompatible => throw _privateConstructorUsedError;
|
||||
ConfigExtendedParams get params => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
@@ -43,8 +268,9 @@ abstract class $UpdateConfigParamsCopyWith<$Res> {
|
||||
$Res call(
|
||||
{@JsonKey(name: "profile-path") String? profilePath,
|
||||
ClashConfig config,
|
||||
@JsonKey(name: "is-patch") bool isPatch,
|
||||
@JsonKey(name: "is-compatible") bool isCompatible});
|
||||
ConfigExtendedParams params});
|
||||
|
||||
$ConfigExtendedParamsCopyWith<$Res> get params;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -62,8 +288,7 @@ class _$UpdateConfigParamsCopyWithImpl<$Res, $Val extends UpdateConfigParams>
|
||||
$Res call({
|
||||
Object? profilePath = freezed,
|
||||
Object? config = null,
|
||||
Object? isPatch = null,
|
||||
Object? isCompatible = null,
|
||||
Object? params = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
profilePath: freezed == profilePath
|
||||
@@ -74,16 +299,20 @@ class _$UpdateConfigParamsCopyWithImpl<$Res, $Val extends UpdateConfigParams>
|
||||
? _value.config
|
||||
: config // ignore: cast_nullable_to_non_nullable
|
||||
as ClashConfig,
|
||||
isPatch: null == isPatch
|
||||
? _value.isPatch
|
||||
: isPatch // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isCompatible: null == isCompatible
|
||||
? _value.isCompatible
|
||||
: isCompatible // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
params: null == params
|
||||
? _value.params
|
||||
: params // ignore: cast_nullable_to_non_nullable
|
||||
as ConfigExtendedParams,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ConfigExtendedParamsCopyWith<$Res> get params {
|
||||
return $ConfigExtendedParamsCopyWith<$Res>(_value.params, (value) {
|
||||
return _then(_value.copyWith(params: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -97,8 +326,10 @@ abstract class _$$UpdateConfigParamsImplCopyWith<$Res>
|
||||
$Res call(
|
||||
{@JsonKey(name: "profile-path") String? profilePath,
|
||||
ClashConfig config,
|
||||
@JsonKey(name: "is-patch") bool isPatch,
|
||||
@JsonKey(name: "is-compatible") bool isCompatible});
|
||||
ConfigExtendedParams params});
|
||||
|
||||
@override
|
||||
$ConfigExtendedParamsCopyWith<$Res> get params;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -114,8 +345,7 @@ class __$$UpdateConfigParamsImplCopyWithImpl<$Res>
|
||||
$Res call({
|
||||
Object? profilePath = freezed,
|
||||
Object? config = null,
|
||||
Object? isPatch = null,
|
||||
Object? isCompatible = null,
|
||||
Object? params = null,
|
||||
}) {
|
||||
return _then(_$UpdateConfigParamsImpl(
|
||||
profilePath: freezed == profilePath
|
||||
@@ -126,14 +356,10 @@ class __$$UpdateConfigParamsImplCopyWithImpl<$Res>
|
||||
? _value.config
|
||||
: config // ignore: cast_nullable_to_non_nullable
|
||||
as ClashConfig,
|
||||
isPatch: null == isPatch
|
||||
? _value.isPatch
|
||||
: isPatch // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isCompatible: null == isCompatible
|
||||
? _value.isCompatible
|
||||
: isCompatible // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
params: null == params
|
||||
? _value.params
|
||||
: params // ignore: cast_nullable_to_non_nullable
|
||||
as ConfigExtendedParams,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -144,8 +370,7 @@ class _$UpdateConfigParamsImpl implements _UpdateConfigParams {
|
||||
const _$UpdateConfigParamsImpl(
|
||||
{@JsonKey(name: "profile-path") this.profilePath,
|
||||
required this.config,
|
||||
@JsonKey(name: "is-patch") required this.isPatch,
|
||||
@JsonKey(name: "is-compatible") required this.isCompatible});
|
||||
required this.params});
|
||||
|
||||
factory _$UpdateConfigParamsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$UpdateConfigParamsImplFromJson(json);
|
||||
@@ -156,15 +381,11 @@ class _$UpdateConfigParamsImpl implements _UpdateConfigParams {
|
||||
@override
|
||||
final ClashConfig config;
|
||||
@override
|
||||
@JsonKey(name: "is-patch")
|
||||
final bool isPatch;
|
||||
@override
|
||||
@JsonKey(name: "is-compatible")
|
||||
final bool isCompatible;
|
||||
final ConfigExtendedParams params;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UpdateConfigParams(profilePath: $profilePath, config: $config, isPatch: $isPatch, isCompatible: $isCompatible)';
|
||||
return 'UpdateConfigParams(profilePath: $profilePath, config: $config, params: $params)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -175,15 +396,12 @@ class _$UpdateConfigParamsImpl implements _UpdateConfigParams {
|
||||
(identical(other.profilePath, profilePath) ||
|
||||
other.profilePath == profilePath) &&
|
||||
(identical(other.config, config) || other.config == config) &&
|
||||
(identical(other.isPatch, isPatch) || other.isPatch == isPatch) &&
|
||||
(identical(other.isCompatible, isCompatible) ||
|
||||
other.isCompatible == isCompatible));
|
||||
(identical(other.params, params) || other.params == params));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, profilePath, config, isPatch, isCompatible);
|
||||
int get hashCode => Object.hash(runtimeType, profilePath, config, params);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@@ -202,11 +420,9 @@ class _$UpdateConfigParamsImpl implements _UpdateConfigParams {
|
||||
|
||||
abstract class _UpdateConfigParams implements UpdateConfigParams {
|
||||
const factory _UpdateConfigParams(
|
||||
{@JsonKey(name: "profile-path") final String? profilePath,
|
||||
required final ClashConfig config,
|
||||
@JsonKey(name: "is-patch") required final bool isPatch,
|
||||
@JsonKey(name: "is-compatible") required final bool isCompatible}) =
|
||||
_$UpdateConfigParamsImpl;
|
||||
{@JsonKey(name: "profile-path") final String? profilePath,
|
||||
required final ClashConfig config,
|
||||
required final ConfigExtendedParams params}) = _$UpdateConfigParamsImpl;
|
||||
|
||||
factory _UpdateConfigParams.fromJson(Map<String, dynamic> json) =
|
||||
_$UpdateConfigParamsImpl.fromJson;
|
||||
@@ -217,11 +433,7 @@ abstract class _UpdateConfigParams implements UpdateConfigParams {
|
||||
@override
|
||||
ClashConfig get config;
|
||||
@override
|
||||
@JsonKey(name: "is-patch")
|
||||
bool get isPatch;
|
||||
@override
|
||||
@JsonKey(name: "is-compatible")
|
||||
bool get isCompatible;
|
||||
ConfigExtendedParams get params;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$UpdateConfigParamsImplCopyWith<_$UpdateConfigParamsImpl> get copyWith =>
|
||||
@@ -1006,6 +1218,151 @@ abstract class _Process implements Process {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Fd _$FdFromJson(Map<String, dynamic> json) {
|
||||
return _Fd.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Fd {
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
int get value => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$FdCopyWith<Fd> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $FdCopyWith<$Res> {
|
||||
factory $FdCopyWith(Fd value, $Res Function(Fd) then) =
|
||||
_$FdCopyWithImpl<$Res, Fd>;
|
||||
@useResult
|
||||
$Res call({int id, int value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$FdCopyWithImpl<$Res, $Val extends Fd> implements $FdCopyWith<$Res> {
|
||||
_$FdCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? value = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
value: null == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$FdImplCopyWith<$Res> implements $FdCopyWith<$Res> {
|
||||
factory _$$FdImplCopyWith(_$FdImpl value, $Res Function(_$FdImpl) then) =
|
||||
__$$FdImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({int id, int value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$FdImplCopyWithImpl<$Res> extends _$FdCopyWithImpl<$Res, _$FdImpl>
|
||||
implements _$$FdImplCopyWith<$Res> {
|
||||
__$$FdImplCopyWithImpl(_$FdImpl _value, $Res Function(_$FdImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? value = null,
|
||||
}) {
|
||||
return _then(_$FdImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
value: null == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$FdImpl implements _Fd {
|
||||
const _$FdImpl({required this.id, required this.value});
|
||||
|
||||
factory _$FdImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$FdImplFromJson(json);
|
||||
|
||||
@override
|
||||
final int id;
|
||||
@override
|
||||
final int value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Fd(id: $id, value: $value)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$FdImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.value, value) || other.value == value));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id, value);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FdImplCopyWith<_$FdImpl> get copyWith =>
|
||||
__$$FdImplCopyWithImpl<_$FdImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$FdImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Fd implements Fd {
|
||||
const factory _Fd({required final int id, required final int value}) =
|
||||
_$FdImpl;
|
||||
|
||||
factory _Fd.fromJson(Map<String, dynamic> json) = _$FdImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get id;
|
||||
@override
|
||||
int get value;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$FdImplCopyWith<_$FdImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ProcessMapItem _$ProcessMapItemFromJson(Map<String, dynamic> json) {
|
||||
return _ProcessMapItem.fromJson(json);
|
||||
}
|
||||
|
||||
@@ -6,13 +6,31 @@ part of '../ffi.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$ConfigExtendedParamsImpl _$$ConfigExtendedParamsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ConfigExtendedParamsImpl(
|
||||
isPatch: json['is-patch'] as bool,
|
||||
isCompatible: json['is-compatible'] as bool,
|
||||
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
|
||||
testUrl: json['test-url'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ConfigExtendedParamsImplToJson(
|
||||
_$ConfigExtendedParamsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'is-patch': instance.isPatch,
|
||||
'is-compatible': instance.isCompatible,
|
||||
'selected-map': instance.selectedMap,
|
||||
'test-url': instance.testUrl,
|
||||
};
|
||||
|
||||
_$UpdateConfigParamsImpl _$$UpdateConfigParamsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$UpdateConfigParamsImpl(
|
||||
profilePath: json['profile-path'] as String?,
|
||||
config: ClashConfig.fromJson(json['config'] as Map<String, dynamic>),
|
||||
isPatch: json['is-patch'] as bool,
|
||||
isCompatible: json['is-compatible'] as bool,
|
||||
params:
|
||||
ConfigExtendedParams.fromJson(json['params'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UpdateConfigParamsImplToJson(
|
||||
@@ -20,8 +38,7 @@ Map<String, dynamic> _$$UpdateConfigParamsImplToJson(
|
||||
<String, dynamic>{
|
||||
'profile-path': instance.profilePath,
|
||||
'config': instance.config,
|
||||
'is-patch': instance.isPatch,
|
||||
'is-compatible': instance.isCompatible,
|
||||
'params': instance.params,
|
||||
};
|
||||
|
||||
_$ChangeProxyParamsImpl _$$ChangeProxyParamsImplFromJson(
|
||||
@@ -58,6 +75,7 @@ const _$MessageTypeEnumMap = {
|
||||
MessageType.now: 'now',
|
||||
MessageType.request: 'request',
|
||||
MessageType.run: 'run',
|
||||
MessageType.loaded: 'loaded',
|
||||
};
|
||||
|
||||
_$DelayImpl _$$DelayImplFromJson(Map<String, dynamic> json) => _$DelayImpl(
|
||||
@@ -93,6 +111,16 @@ Map<String, dynamic> _$$ProcessImplToJson(_$ProcessImpl instance) =>
|
||||
'metadata': instance.metadata,
|
||||
};
|
||||
|
||||
_$FdImpl _$$FdImplFromJson(Map<String, dynamic> json) => _$FdImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
value: (json['value'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$FdImplToJson(_$FdImpl instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'value': instance.value,
|
||||
};
|
||||
|
||||
_$ProcessMapItemImpl _$$ProcessMapItemImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ProcessMapItemImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
|
||||
@@ -161,6 +161,7 @@ mixin _$CheckIpSelectorState {
|
||||
bool get isInit => throw _privateConstructorUsedError;
|
||||
bool get isStart => throw _privateConstructorUsedError;
|
||||
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
|
||||
num get checkIpNum => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$CheckIpSelectorStateCopyWith<CheckIpSelectorState> get copyWith =>
|
||||
@@ -173,7 +174,11 @@ abstract class $CheckIpSelectorStateCopyWith<$Res> {
|
||||
$Res Function(CheckIpSelectorState) then) =
|
||||
_$CheckIpSelectorStateCopyWithImpl<$Res, CheckIpSelectorState>;
|
||||
@useResult
|
||||
$Res call({bool isInit, bool isStart, Map<String, String> selectedMap});
|
||||
$Res call(
|
||||
{bool isInit,
|
||||
bool isStart,
|
||||
Map<String, String> selectedMap,
|
||||
num checkIpNum});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -193,6 +198,7 @@ class _$CheckIpSelectorStateCopyWithImpl<$Res,
|
||||
Object? isInit = null,
|
||||
Object? isStart = null,
|
||||
Object? selectedMap = null,
|
||||
Object? checkIpNum = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isInit: null == isInit
|
||||
@@ -207,6 +213,10 @@ class _$CheckIpSelectorStateCopyWithImpl<$Res,
|
||||
? _value.selectedMap
|
||||
: selectedMap // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, String>,
|
||||
checkIpNum: null == checkIpNum
|
||||
? _value.checkIpNum
|
||||
: checkIpNum // ignore: cast_nullable_to_non_nullable
|
||||
as num,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -219,7 +229,11 @@ abstract class _$$CheckIpSelectorStateImplCopyWith<$Res>
|
||||
__$$CheckIpSelectorStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool isInit, bool isStart, Map<String, String> selectedMap});
|
||||
$Res call(
|
||||
{bool isInit,
|
||||
bool isStart,
|
||||
Map<String, String> selectedMap,
|
||||
num checkIpNum});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -236,6 +250,7 @@ class __$$CheckIpSelectorStateImplCopyWithImpl<$Res>
|
||||
Object? isInit = null,
|
||||
Object? isStart = null,
|
||||
Object? selectedMap = null,
|
||||
Object? checkIpNum = null,
|
||||
}) {
|
||||
return _then(_$CheckIpSelectorStateImpl(
|
||||
isInit: null == isInit
|
||||
@@ -250,6 +265,10 @@ class __$$CheckIpSelectorStateImplCopyWithImpl<$Res>
|
||||
? _value._selectedMap
|
||||
: selectedMap // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, String>,
|
||||
checkIpNum: null == checkIpNum
|
||||
? _value.checkIpNum
|
||||
: checkIpNum // ignore: cast_nullable_to_non_nullable
|
||||
as num,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -260,7 +279,8 @@ class _$CheckIpSelectorStateImpl implements _CheckIpSelectorState {
|
||||
const _$CheckIpSelectorStateImpl(
|
||||
{required this.isInit,
|
||||
required this.isStart,
|
||||
required final Map<String, String> selectedMap})
|
||||
required final Map<String, String> selectedMap,
|
||||
required this.checkIpNum})
|
||||
: _selectedMap = selectedMap;
|
||||
|
||||
@override
|
||||
@@ -275,9 +295,12 @@ class _$CheckIpSelectorStateImpl implements _CheckIpSelectorState {
|
||||
return EqualUnmodifiableMapView(_selectedMap);
|
||||
}
|
||||
|
||||
@override
|
||||
final num checkIpNum;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CheckIpSelectorState(isInit: $isInit, isStart: $isStart, selectedMap: $selectedMap)';
|
||||
return 'CheckIpSelectorState(isInit: $isInit, isStart: $isStart, selectedMap: $selectedMap, checkIpNum: $checkIpNum)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -288,12 +311,14 @@ class _$CheckIpSelectorStateImpl implements _CheckIpSelectorState {
|
||||
(identical(other.isInit, isInit) || other.isInit == isInit) &&
|
||||
(identical(other.isStart, isStart) || other.isStart == isStart) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._selectedMap, _selectedMap));
|
||||
.equals(other._selectedMap, _selectedMap) &&
|
||||
(identical(other.checkIpNum, checkIpNum) ||
|
||||
other.checkIpNum == checkIpNum));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, isInit, isStart,
|
||||
const DeepCollectionEquality().hash(_selectedMap));
|
||||
const DeepCollectionEquality().hash(_selectedMap), checkIpNum);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@@ -306,10 +331,10 @@ class _$CheckIpSelectorStateImpl implements _CheckIpSelectorState {
|
||||
|
||||
abstract class _CheckIpSelectorState implements CheckIpSelectorState {
|
||||
const factory _CheckIpSelectorState(
|
||||
{required final bool isInit,
|
||||
required final bool isStart,
|
||||
required final Map<String, String> selectedMap}) =
|
||||
_$CheckIpSelectorStateImpl;
|
||||
{required final bool isInit,
|
||||
required final bool isStart,
|
||||
required final Map<String, String> selectedMap,
|
||||
required final num checkIpNum}) = _$CheckIpSelectorStateImpl;
|
||||
|
||||
@override
|
||||
bool get isInit;
|
||||
@@ -318,6 +343,8 @@ abstract class _CheckIpSelectorState implements CheckIpSelectorState {
|
||||
@override
|
||||
Map<String, String> get selectedMap;
|
||||
@override
|
||||
num get checkIpNum;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
|
||||
@@ -19,6 +19,7 @@ class CheckIpSelectorState with _$CheckIpSelectorState {
|
||||
required bool isInit,
|
||||
required bool isStart,
|
||||
required SelectedMap selectedMap,
|
||||
required num checkIpNum
|
||||
}) = _CheckIpSelectorState;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
import 'models/models.dart';
|
||||
@@ -15,6 +16,7 @@ import 'common/common.dart';
|
||||
class GlobalState {
|
||||
Timer? timer;
|
||||
Timer? groupsUpdateTimer;
|
||||
late PackageInfo packageInfo;
|
||||
Function? updateCurrentDelayDebounce;
|
||||
PageController? pageController;
|
||||
final navigatorKey = GlobalKey<NavigatorState>();
|
||||
@@ -43,13 +45,18 @@ 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,
|
||||
params: ConfigExtendedParams(
|
||||
isPatch: isPatch,
|
||||
isCompatible: config.isCompatible,
|
||||
selectedMap: config.currentSelectedMap,
|
||||
testUrl: config.testUrl,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (res.isNotEmpty) throw res;
|
||||
}
|
||||
|
||||
@@ -79,7 +86,9 @@ class GlobalState {
|
||||
appState: appState,
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
).then((_){
|
||||
appController.appState.checkIpNum++;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> stopSystemProxy() async {
|
||||
@@ -87,7 +96,7 @@ class GlobalState {
|
||||
stopListenUpdate();
|
||||
}
|
||||
|
||||
Future<void> applyProfile({
|
||||
Future applyProfile({
|
||||
required AppState appState,
|
||||
required Config config,
|
||||
required ClashConfig clashConfig,
|
||||
@@ -98,11 +107,6 @@ class GlobalState {
|
||||
isPatch: false,
|
||||
);
|
||||
await updateGroups(appState);
|
||||
changeProxy(
|
||||
appState: appState,
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
}
|
||||
|
||||
init({
|
||||
@@ -120,25 +124,6 @@ class GlobalState {
|
||||
updateCoreVersionInfo(appState);
|
||||
}
|
||||
|
||||
changeProxy({
|
||||
required AppState appState,
|
||||
required Config config,
|
||||
required ClashConfig clashConfig,
|
||||
}) {
|
||||
if (config.profiles.isEmpty) {
|
||||
stopSystemProxy();
|
||||
return;
|
||||
}
|
||||
config.currentSelectedMap.forEach((key, value) {
|
||||
clashCore.changeProxy(
|
||||
ChangeProxyParams(
|
||||
groupName: key,
|
||||
proxyName: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateGroups(AppState appState) async {
|
||||
appState.groups = await clashCore.getProxiesGroups();
|
||||
}
|
||||
@@ -263,7 +248,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],
|
||||
|
||||
@@ -51,8 +51,9 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
|
||||
}
|
||||
|
||||
@override
|
||||
void onTun(String fd) {
|
||||
proxyManager.setProtect(int.parse(fd));
|
||||
Future<void> onTun(Fd fd) async {
|
||||
await proxyManager.setProtect(fd.value);
|
||||
clashCore.setFdMap(fd.id);
|
||||
super.onTun(fd);
|
||||
}
|
||||
|
||||
@@ -75,8 +76,18 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
|
||||
}
|
||||
|
||||
@override
|
||||
void onRun(String runTime) async {
|
||||
// proxy?.updateStartTime();
|
||||
super.onRun(runTime);
|
||||
void onLoaded(String groupName) {
|
||||
final appController = globalState.appController;
|
||||
final currentSelectedMap = appController.config.currentSelectedMap;
|
||||
final proxyName = currentSelectedMap[groupName];
|
||||
if (proxyName == null) return;
|
||||
clashCore.changeProxy(
|
||||
ChangeProxyParams(
|
||||
groupName: groupName,
|
||||
proxyName: proxyName,
|
||||
),
|
||||
);
|
||||
appController.appState.checkIpNum++;
|
||||
super.onLoaded(proxyName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1162,7 +1162,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
win32:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: win32
|
||||
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
|
||||
|
||||
@@ -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.32
|
||||
version: 0.8.34+202407041
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
@@ -16,7 +16,6 @@ dependencies:
|
||||
shared_preferences: ^2.2.0
|
||||
provider: ^6.0.5
|
||||
window_manager: ^0.3.8
|
||||
ffi: ^2.1.0
|
||||
dynamic_color: ^1.7.0
|
||||
proxy:
|
||||
path: plugins/proxy
|
||||
@@ -41,6 +40,8 @@ dependencies:
|
||||
country_flags: ^2.2.0
|
||||
re_editor: ^0.3.0
|
||||
re_highlight: ^0.0.3
|
||||
win32: ^5.5.1
|
||||
ffi: ^2.1.2
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
@@ -17,6 +17,11 @@ add_executable(${BINARY_NAME} WIN32
|
||||
"Runner.rc"
|
||||
"runner.exe.manifest"
|
||||
)
|
||||
|
||||
# add_executable(service
|
||||
# "service.cpp"
|
||||
# )
|
||||
|
||||
# Apply the standard set of build settings. This can be removed for applications
|
||||
# that need different build settings.
|
||||
apply_standard_settings(${BINARY_NAME})
|
||||
|
||||
152
windows/runner/service.cpp
Normal file
152
windows/runner/service.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <string>
|
||||
|
||||
#define SERVICE_NAME _T("MyService")
|
||||
|
||||
SERVICE_STATUS g_ServiceStatus = {0};
|
||||
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
|
||||
HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE;
|
||||
|
||||
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
|
||||
VOID WINAPI ServiceCtrlHandler(DWORD);
|
||||
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);
|
||||
|
||||
int _tmain(int argc, TCHAR *argv[])
|
||||
{
|
||||
SERVICE_TABLE_ENTRY ServiceTable[] =
|
||||
{
|
||||
{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
|
||||
{
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
|
||||
{
|
||||
DWORD Status = E_FAIL;
|
||||
|
||||
g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
|
||||
|
||||
if (g_StatusHandle == NULL)
|
||||
{
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
|
||||
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
||||
g_ServiceStatus.dwWin32ExitCode = 0;
|
||||
g_ServiceStatus.dwServiceSpecificExitCode = 0;
|
||||
g_ServiceStatus.dwCheckPoint = 0;
|
||||
|
||||
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
|
||||
{
|
||||
OutputDebugString(_T("My Service: ServiceMain: SetServiceStatus returned error"));
|
||||
}
|
||||
|
||||
g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (g_ServiceStopEvent == NULL)
|
||||
{
|
||||
g_ServiceStatus.dwControlsAccepted = 0;
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
g_ServiceStatus.dwWin32ExitCode = GetLastError();
|
||||
g_ServiceStatus.dwCheckPoint = 1;
|
||||
|
||||
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
|
||||
{
|
||||
OutputDebugString(_T("My Service: ServiceMain: SetServiceStatus returned error"));
|
||||
}
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
||||
g_ServiceStatus.dwCheckPoint = 0;
|
||||
g_ServiceStatus.dwWaitHint = 0;
|
||||
|
||||
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
|
||||
{
|
||||
OutputDebugString(_T("My Service: ServiceMain: SetServiceStatus returned error"));
|
||||
}
|
||||
|
||||
HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
|
||||
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
|
||||
CloseHandle(g_ServiceStopEvent);
|
||||
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
g_ServiceStatus.dwCheckPoint = 3;
|
||||
|
||||
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
|
||||
{
|
||||
OutputDebugString(_T("My Service: ServiceMain: SetServiceStatus returned error"));
|
||||
}
|
||||
|
||||
EXIT:
|
||||
return;
|
||||
}
|
||||
|
||||
VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
|
||||
{
|
||||
switch(CtrlCode)
|
||||
{
|
||||
case SERVICE_CONTROL_STOP:
|
||||
if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
|
||||
break;
|
||||
|
||||
g_ServiceStatus.dwControlsAccepted = 0;
|
||||
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||||
g_ServiceStatus.dwWin32ExitCode = 0;
|
||||
g_ServiceStatus.dwCheckPoint = 4;
|
||||
|
||||
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
|
||||
{
|
||||
OutputDebugString(_T("My Service: ServiceCtrlHandler: SetServiceStatus returned error"));
|
||||
}
|
||||
|
||||
SetEvent(g_ServiceStopEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
|
||||
{
|
||||
while(WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
|
||||
{
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
// 启动 "C:\path\to\your\executable.exe"
|
||||
if(!CreateProcess(NULL, _T("C:\\path\\to\\your\\executable.exe"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
|
||||
{
|
||||
OutputDebugString(_T("CreateProcess failed"));
|
||||
}
|
||||
|
||||
// 等待进程结束
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
||||
// 关闭进程和线程句柄
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
// 每隔一段时间检查一次,这里设置为60秒
|
||||
Sleep(60000);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user