Compare commits

...

1 Commits

Author SHA1 Message Date
chen08209
35f7279fcb Add tcp concurrent switch
Add system proxy switch

Add geodata loader switch

Add external controller switch

Add auto gc on trim memory

Fix android notification error
2024-06-13 17:04:57 +08:00
27 changed files with 536 additions and 152 deletions

View File

@@ -1,6 +1,7 @@
package com.follow.clash
import androidx.lifecycle.MutableLiveData
import com.follow.clash.plugins.AppPlugin
import com.follow.clash.plugins.TilePlugin
import io.flutter.embedding.engine.FlutterEngine
import java.util.Date
@@ -18,6 +19,9 @@ class GlobalState {
var flutterEngine: FlutterEngine? = null
fun getCurrentTilePlugin(): TilePlugin? =
flutterEngine?.plugins?.get(TilePlugin::class.java) as TilePlugin?
fun getCurrentAppPlugin(): AppPlugin? =
flutterEngine?.plugins?.get(AppPlugin::class.java) as AppPlugin?
}
}

View File

@@ -11,7 +11,8 @@ data class AccessControl(
val rejectList: List<String>,
)
data class Props (
data class Props(
val accessControl: AccessControl?,
val allowBypass: Boolean?,
val systemProxy: Boolean?,
)

View File

@@ -143,7 +143,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
}
private suspend fun getPackages(): String {
return withContext(Dispatchers.Default){
return withContext(Dispatchers.Default) {
val packageManager = context?.packageManager
val packages: List<Package>? =
packageManager?.getInstalledPackages(PackageManager.GET_META_DATA)?.filter {
@@ -162,6 +162,10 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
}
}
fun requestGc() {
channel.invokeMethod("gc",null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity;
scope = CoroutineScope(Dispatchers.Default)

View File

@@ -15,7 +15,6 @@ import androidx.core.app.NotificationCompat
import com.follow.clash.GlobalState
import com.follow.clash.MainActivity
import com.follow.clash.R
import com.follow.clash.models.AccessControl
import com.follow.clash.models.AccessControlMode
import com.follow.clash.models.Props
@@ -81,7 +80,7 @@ class FlClashVpnService : VpnService() {
if (props?.allowBypass == true) {
allowBypass()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && props?.systemProxy == true) {
setHttpProxy(
ProxyInfo.buildDirectProxy(
"127.0.0.1",
@@ -135,14 +134,20 @@ class FlClashVpnService : VpnService() {
}
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
GlobalState.getCurrentAppPlugin()?.requestGc()
}
fun startForeground(title: String, content: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(channel)
channel.setShowBadge(false)
var channel = manager?.getNotificationChannel(CHANNEL)
if (channel == null) {
channel =
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
manager?.createNotificationChannel(channel)
}
}
val notification =
notificationBuilder.setContentTitle(title).setContentText(content).build()

View File

@@ -10,6 +10,7 @@ import (
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant/provider"
"github.com/metacubex/mihomo/dns"
"github.com/metacubex/mihomo/hub"
"github.com/metacubex/mihomo/hub/executor"
"github.com/metacubex/mihomo/listener"
"github.com/metacubex/mihomo/log"
@@ -325,6 +326,8 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.ExternalUI = ""
targetConfig.Interface = ""
targetConfig.ExternalUIURL = ""
targetConfig.TCPConcurrent = patchConfig.TCPConcurrent
targetConfig.UnifiedDelay = patchConfig.UnifiedDelay
targetConfig.GeodataMode = false
targetConfig.IPv6 = patchConfig.IPv6
targetConfig.LogLevel = patchConfig.LogLevel
@@ -338,7 +341,7 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.Tun.Device = patchConfig.Tun.Device
//targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack
//targetConfig.Tun.Stack = patchConfig.Tun.Stack
targetConfig.GeodataLoader = "standard"
targetConfig.GeodataLoader = patchConfig.GeodataLoader
targetConfig.Profile.StoreSelected = false
if targetConfig.DNS.Enable == false {
targetConfig.DNS = patchConfig.DNS
@@ -410,7 +413,7 @@ func applyConfig(isPatch bool) {
if isPatch {
patchConfig(cfg.General)
} else {
executor.ApplyConfig(cfg, true)
hub.UltraApplyConfig(cfg, true)
hcCompatibleProvider(tunnel.Providers())
}
}

View File

@@ -17,6 +17,7 @@ require (
github.com/RyuaNerin/go-krypto v1.2.4 // indirect
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
@@ -30,6 +31,9 @@ require (
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-chi/chi/v5 v5.0.12 // indirect
github.com/go-chi/cors v1.2.1 // indirect
github.com/go-chi/render v1.0.3 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect

View File

@@ -9,6 +9,8 @@ github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
@@ -35,16 +37,25 @@ github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIF
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c/go.mod h1:ETASDWf/FmEb6Ysrtd1QhjNedUU/ZQxBCRLh60bQ/UI=
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@@ -60,6 +71,7 @@ github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM=
github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -69,6 +81,7 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -83,7 +96,9 @@ github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
@@ -125,6 +140,7 @@ github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:U
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=
github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=
@@ -146,6 +162,7 @@ github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
@@ -249,6 +266,7 @@ golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
@@ -263,6 +281,7 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -60,6 +60,14 @@ func shutdownClash() bool {
return true
}
//export forceGc
func forceGc() {
go func() {
log.Infoln("[APP] request force GC")
runtime.GC()
}()
}
//export validateConfig
func validateConfig(s *C.char, port C.longlong) {
i := int64(port)

View File

@@ -210,6 +210,10 @@ class ClashCore {
clashFFI.startTUN(fd);
}
requestGc(){
clashFFI.forceGc();
}
void stopTun() {
clashFFI.stopTun();
}

View File

@@ -893,6 +893,14 @@ class ClashFFI {
_lookup<ffi.NativeFunction<GoUint8 Function()>>('shutdownClash');
late final _shutdownClash = _shutdownClashPtr.asFunction<int Function()>();
void forceGc() {
return _forceGc();
}
late final _forceGcPtr =
_lookup<ffi.NativeFunction<ffi.Void Function()>>('forceGc');
late final _forceGc = _forceGcPtr.asFunction<void Function()>();
void validateConfig(
ffi.Pointer<ffi.Char> s,
int port,
@@ -1122,7 +1130,7 @@ class ClashFFI {
_lookup<ffi.NativeFunction<ffi.Void Function()>>('stopLog');
late final _stopLog = _stopLogPtr.asFunction<void Function()>();
int startTUN(
void startTUN(
int fd,
) {
return _startTUN(
@@ -1131,8 +1139,8 @@ class ClashFFI {
}
late final _startTUNPtr =
_lookup<ffi.NativeFunction<GoUint8 Function(ffi.Int)>>('startTUN');
late final _startTUN = _startTUNPtr.asFunction<int Function(int)>();
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>('startTUN');
late final _startTUN = _startTUNPtr.asFunction<void Function(int)>();
int updateMarkSocketPort(
int markSocketPort,

View File

@@ -21,6 +21,8 @@ const repository = "chen08209/FlClash";
const defaultExternalController = "127.0.0.1:9090";
const maxMobileWidth = 600;
const maxLaptopWidth = 840;
const geodataLoaderMemconservative = "memconservative";
const geodataLoaderStandard = "standard";
final filter = ImageFilter.blur(
sigmaX: 5,
sigmaY: 5,

View File

@@ -46,9 +46,110 @@ class _ConfigFragmentState extends State<ConfigFragment> {
globalState.appController.updateClashConfigDebounce();
}
@override
Widget build(BuildContext context) {
List<Widget> items = [
_buildAppSection() {
final items = [
if (Platform.isAndroid)
Selector<Config, bool>(
selector: (_, config) => config.allowBypass,
builder: (_, allowBypass, __) {
return ListItem.switchItem(
leading: const Icon(Icons.arrow_forward_outlined),
title: Text(appLocalizations.allowBypass),
subtitle: Text(appLocalizations.allowBypassDesc),
delegate: SwitchDelegate(
value: allowBypass,
onChanged: (bool value) async {
final appController = globalState.appController;
appController.config.allowBypass = value;
},
),
);
},
),
if (Platform.isAndroid)
Selector<Config, bool>(
selector: (_, config) => config.systemProxy,
builder: (_, systemProxy, __) {
return ListItem.switchItem(
leading: const Icon(Icons.settings_ethernet),
title: Text(appLocalizations.systemProxy),
subtitle: Text(appLocalizations.systemProxyDesc),
delegate: SwitchDelegate(
value: systemProxy,
onChanged: (bool value) async {
final appController = globalState.appController;
appController.config.systemProxy = value;
},
),
);
},
),
Selector<Config, bool>(
selector: (_, config) => config.isCompatible,
builder: (_, isCompatible, __) {
return ListItem.switchItem(
leading: const Icon(Icons.expand_outlined),
title: Text(appLocalizations.compatible),
subtitle: Text(appLocalizations.compatibleDesc),
delegate: SwitchDelegate(
value: isCompatible,
onChanged: (bool value) async {
final appController = globalState.appController;
appController.config.isCompatible = value;
await appController.updateClashConfig(isPatch: false);
await appController.updateGroups();
appController.changeProxy();
},
),
);
},
),
];
return Section(
title: appLocalizations.app,
child: Column(
children: [
for (final item in items) ...[
item,
if (items.last != item)
const Divider(
height: 0,
)
]
],
),
);
}
_buildGeneralSection() {
final items = [
Padding(
padding: kMaterialListPadding,
child: Selector<ClashConfig, LogLevel>(
selector: (_, clashConfig) => clashConfig.logLevel,
builder: (_, value, __) {
return ListItem(
leading: const Icon(Icons.info_outline),
title: Text(appLocalizations.logLevel),
trailing: SizedBox(
height: 48,
child: DropdownMenu<LogLevel>(
width: 124,
initialSelection: value,
dropdownMenuEntries: [
for (final logLevel in LogLevel.values)
DropdownMenuEntry<LogLevel>(
value: logLevel,
label: logLevel.name,
)
],
onSelected: _updateLoglevel,
),
),
);
},
),
),
Selector<ClashConfig, int>(
selector: (_, clashConfig) => clashConfig.mixedPort,
builder: (_, mixedPort, __) {
@@ -56,9 +157,9 @@ class _ConfigFragmentState extends State<ConfigFragment> {
onTab: () {
_modifyMixedPort(mixedPort);
},
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
leading: const Icon(Icons.adjust_outlined),
title: Text(appLocalizations.proxyPort),
subtitle: Text(appLocalizations.proxyPortDesc),
trailing: FilledButton.tonal(
onPressed: () {
_modifyMixedPort(mixedPort);
@@ -106,124 +207,113 @@ class _ConfigFragmentState extends State<ConfigFragment> {
);
},
),
// if (system.isDesktop)
// Selector<ClashConfig, bool>(
// selector: (_, clashConfig) => clashConfig.tun.enable,
// builder: (_, tunEnable, __) {
// return ListItem.switchItem(
// leading: const Icon(Icons.support),
// title: Text(appLocalizations.tun),
// subtitle: Text(appLocalizations.tunDesc),
// delegate: SwitchDelegate(
// value: tunEnable,
// onChanged: (bool value) async {
// final clashConfig = context.read<ClashConfig>();
// clashConfig.tun = Tun(enable: value);
// globalState.appController.updateClashConfigDebounce();
// },
// ),
// );
// },
// ),
if (Platform.isAndroid)
Selector<Config, bool>(
selector: (_, config) => config.allowBypass,
builder: (_, allowBypass, __) {
return ListItem.switchItem(
leading: const Icon(Icons.double_arrow),
title: Text(appLocalizations.allowBypass),
subtitle: Text(appLocalizations.allowBypassDesc),
delegate: SwitchDelegate(
value: allowBypass,
onChanged: (bool value) async {
final appController = globalState.appController;
appController.config.allowBypass = value;
},
),
);
},
),
Selector<Config, bool>(
selector: (_, config) => config.isCompatible,
builder: (_, isCompatible, __) {
Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.unifiedDelay,
builder: (_, unifiedDelay, __) {
return ListItem.switchItem(
leading: const Icon(Icons.expand_outlined),
title: Text(appLocalizations.compatible),
subtitle: Text(appLocalizations.compatibleDesc),
leading: const Icon(Icons.compress_outlined),
title: Text(appLocalizations.unifiedDelay),
subtitle: Text(appLocalizations.unifiedDelayDesc),
delegate: SwitchDelegate(
value: isCompatible,
value: unifiedDelay,
onChanged: (bool value) async {
final appController = globalState.appController;
appController.config.isCompatible = value;
await appController.updateClashConfig(isPatch: false);
await appController.updateGroups();
appController.changeProxy();
appController.clashConfig.unifiedDelay = value;
appController.updateClashConfigDebounce();
},
),
);
},
),
// Selector<ClashConfig, bool>(
// selector: (_, clashConfig) => clashConfig.externalController.isNotEmpty,
// builder: (_, hasExternalController, __) {
// return ListItem.switchItem(
// leading: const Icon(Icons.api_outlined),
// title: Text(appLocalizations.externalController),
// subtitle: Text(appLocalizations.externalControllerDesc),
// delegate: SwitchDelegate(
// value: hasExternalController,
// onChanged: (bool value) async {
// final appController = globalState.appController;
// appController.clashConfig.externalController =
// value ? defaultExternalController : '';
// await appController.updateClashConfig(
// isPatch: false,
// );
// },
// ),
// );
// },
// ),
Padding(
padding: kMaterialListPadding,
child: Selector<ClashConfig, LogLevel>(
selector: (_, clashConfig) => clashConfig.logLevel,
builder: (_, value, __) {
return ListItem(
leading: const Icon(Icons.info_outline),
title: Text(appLocalizations.logLevel),
trailing: SizedBox(
height: 48,
child: DropdownMenu<LogLevel>(
width: 124,
initialSelection: value,
dropdownMenuEntries: [
for (final logLevel in LogLevel.values)
DropdownMenuEntry<LogLevel>(
value: logLevel,
label: logLevel.name,
)
],
onSelected: _updateLoglevel,
),
),
);
},
),
Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.tcpConcurrent,
builder: (_, tcpConcurrent, __) {
return ListItem.switchItem(
leading: const Icon(Icons.double_arrow_outlined),
title: Text(appLocalizations.tcpConcurrent),
subtitle: Text(appLocalizations.tcpConcurrentDesc),
delegate: SwitchDelegate(
value: tcpConcurrent,
onChanged: (bool value) async {
final appController = globalState.appController;
appController.clashConfig.tcpConcurrent = value;
appController.updateClashConfigDebounce();
},
),
);
},
),
Selector<ClashConfig, bool>(
selector: (_, clashConfig) =>
clashConfig.geodataLoader == geodataLoaderMemconservative,
builder: (_, memconservative, __) {
return ListItem.switchItem(
leading: const Icon(Icons.memory),
title: Text(appLocalizations.geodataLoader),
subtitle: Text(appLocalizations.geodataLoaderDesc),
delegate: SwitchDelegate(
value: memconservative,
onChanged: (bool value) async {
final appController = globalState.appController;
appController.clashConfig.geodataLoader = value
? geodataLoaderMemconservative
: geodataLoaderStandard;
appController.updateClashConfigDebounce;
},
),
);
},
),
Selector<ClashConfig, bool>(
selector: (_, clashConfig) => clashConfig.externalController.isNotEmpty,
builder: (_, hasExternalController, __) {
return ListItem.switchItem(
leading: const Icon(Icons.api_outlined),
title: Text(appLocalizations.externalController),
subtitle: Text(appLocalizations.externalControllerDesc),
delegate: SwitchDelegate(
value: hasExternalController,
onChanged: (bool value) async {
final appController = globalState.appController;
appController.clashConfig.externalController =
value ? defaultExternalController : '';
appController.updateClashConfigDebounce;
},
),
);
},
),
];
return ListView.separated(
return Section(
title: appLocalizations.general,
child: Column(
children: [
for (final item in items) ...[
item,
if (items.last != item)
const Divider(
height: 0,
)
]
],
),
);
}
@override
Widget build(BuildContext context) {
List<Widget> items = [
_buildAppSection(),
_buildGeneralSection(),
];
return ListView.builder(
padding: const EdgeInsets.only(bottom: 16),
itemBuilder: (_, index) {
return Container(
alignment: Alignment.center,
child: items[index],
);
},
separatorBuilder: (_, __) {
return const Divider(
height: 0,
);
},
itemCount: items.length,
);
}

View File

@@ -152,6 +152,7 @@ class _EditProfileState extends State<EditProfile> {
vertical: 16,
),
child: ListView.separated(
primary: true,
itemBuilder: (_, index) {
return items[index];
},

View File

@@ -166,6 +166,7 @@ class _ToolboxFragmentState extends State<ToolsFragment> {
delegate: OpenDelegate(
title: appLocalizations.override,
widget: const ConfigFragment(),
extendPageWidth: 360,
),
),
ListItem.open(

View File

@@ -111,6 +111,7 @@
"noMoreInfoDesc": "No more info",
"profileParseErrorDesc": "profile parse error",
"proxyPort": "ProxyPort",
"proxyPortDesc": "Set the clash listening port",
"port": "Port",
"logLevel": "LogLevel",
"show": "Show",
@@ -163,8 +164,17 @@
"ipCheckTimeout": "Ip check timeout",
"search": "Search",
"allowBypass": "Allow applications to bypass VPN",
"allowBypassDesc": "Enabled to some applications can bypass VPN",
"allowBypassDesc": "Some apps can bypass VPN when turned on",
"externalController": "ExternalController",
"externalControllerDesc": "Enabled to control the clash on port 9090",
"ipv6Desc": "Enabled to will allow it to receive ipv6 traffic"
"externalControllerDesc": "Once enabled, the clash kernel can be controlled on port 9090",
"ipv6Desc": "When turned on it will be able to receive ipv6 traffic",
"app": "App",
"general": "General",
"systemProxyDesc": "Attach HTTP proxy to VpnService",
"unifiedDelay": "Unified delay",
"unifiedDelayDesc": "Remove extra delays such as handshaking",
"tcpConcurrent": "Tcp concurrent",
"tcpConcurrentDesc": "Enabling it will allow tcp concurrency",
"geodataLoader": "Geo Low Memory Mode",
"geodataLoaderDesc": "Enabling will use the Geo low memory loader"
}

View File

@@ -111,6 +111,7 @@
"noMoreInfoDesc": "暂无更多信息",
"profileParseErrorDesc": "配置文件解析错误",
"proxyPort": "代理端口",
"proxyPortDesc": "设置clash监听端口",
"port": "端口",
"logLevel": "日志等级",
"show": "显示",
@@ -165,6 +166,15 @@
"allowBypass": "允许应用绕过vpn",
"allowBypassDesc": "开启后部分应用可绕过VPN",
"externalController": "外部控制器",
"externalControllerDesc": "开启后通过9090端口控制clash内核",
"ipv6Desc": "开启后将可以接收ipv6流量"
"externalControllerDesc": "开启后将可以通过9090端口控制clash内核",
"ipv6Desc": "开启后将可以接收ipv6流量",
"app": "应用",
"general": "基础",
"systemProxyDesc": "为VpnService附加HTTP代理",
"unifiedDelay": "统一延迟",
"unifiedDelayDesc": "去除握手等额外延迟",
"tcpConcurrent": "TCP并发",
"tcpConcurrentDesc": "开启后允许tcp并发",
"geodataLoader": "Geo低内存模式",
"geodataLoaderDesc": "开启将使用Geo低内存加载器"
}

View File

@@ -43,10 +43,11 @@ class MessageLookup extends MessageLookupByLibrary {
"allowBypass": MessageLookupByLibrary.simpleMessage(
"Allow applications to bypass VPN"),
"allowBypassDesc": MessageLookupByLibrary.simpleMessage(
"Enabled to some applications can bypass VPN"),
"Some apps can bypass VPN when turned on"),
"allowLan": MessageLookupByLibrary.simpleMessage("AllowLan"),
"allowLanDesc": MessageLookupByLibrary.simpleMessage(
"Allow access proxy through the LAN"),
"app": MessageLookupByLibrary.simpleMessage("App"),
"appAccessControl":
MessageLookupByLibrary.simpleMessage("App access control"),
"application": MessageLookupByLibrary.simpleMessage("Application"),
@@ -119,7 +120,7 @@ class MessageLookup extends MessageLookupByLibrary {
"externalController":
MessageLookupByLibrary.simpleMessage("ExternalController"),
"externalControllerDesc": MessageLookupByLibrary.simpleMessage(
"Enabled to control the clash on port 9090"),
"Once enabled, the clash kernel can be controlled on port 9090"),
"externalResources":
MessageLookupByLibrary.simpleMessage("External resources"),
"file": MessageLookupByLibrary.simpleMessage("File"),
@@ -127,7 +128,12 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Directly upload profile"),
"filterSystemApp":
MessageLookupByLibrary.simpleMessage("Filter system app"),
"general": MessageLookupByLibrary.simpleMessage("General"),
"geoData": MessageLookupByLibrary.simpleMessage("GeoData"),
"geodataLoader":
MessageLookupByLibrary.simpleMessage("Geo Low Memory Mode"),
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage(
"Enabling will use the Geo low memory loader"),
"global": MessageLookupByLibrary.simpleMessage("Global"),
"goDownload": MessageLookupByLibrary.simpleMessage("Go to download"),
"hours": MessageLookupByLibrary.simpleMessage("Hours"),
@@ -136,7 +142,7 @@ class MessageLookup extends MessageLookupByLibrary {
"ipCheckTimeout":
MessageLookupByLibrary.simpleMessage("Ip check timeout"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
"Enabled to will allow it to receive ipv6 traffic"),
"When turned on it will be able to receive ipv6 traffic"),
"just": MessageLookupByLibrary.simpleMessage("Just"),
"language": MessageLookupByLibrary.simpleMessage("Language"),
"light": MessageLookupByLibrary.simpleMessage("Light"),
@@ -205,6 +211,8 @@ class MessageLookup extends MessageLookupByLibrary {
"project": MessageLookupByLibrary.simpleMessage("Project"),
"proxies": MessageLookupByLibrary.simpleMessage("Proxies"),
"proxyPort": MessageLookupByLibrary.simpleMessage("ProxyPort"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage(
"Set the clash listening port"),
"qrcode": MessageLookupByLibrary.simpleMessage("QR code"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage(
"Scan QR code to obtain profile"),
@@ -234,9 +242,14 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."),
"submit": MessageLookupByLibrary.simpleMessage("Submit"),
"systemProxy": MessageLookupByLibrary.simpleMessage("SystemProxy"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
"Attach HTTP proxy to VpnService"),
"tabAnimation": MessageLookupByLibrary.simpleMessage("Tab animation"),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
"When enabled, the home tab will add a toggle animation"),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("Tcp concurrent"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage(
"Enabling it will allow tcp concurrency"),
"theme": MessageLookupByLibrary.simpleMessage("Theme"),
"themeColor": MessageLookupByLibrary.simpleMessage("Theme color"),
"themeDesc": MessageLookupByLibrary.simpleMessage(
@@ -251,6 +264,9 @@ class MessageLookup extends MessageLookupByLibrary {
"unableToUpdateCurrentProfileDesc":
MessageLookupByLibrary.simpleMessage(
"unable to update current profile"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage("Unified delay"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage(
"Remove extra delays such as handshaking"),
"unknown": MessageLookupByLibrary.simpleMessage("Unknown"),
"update": MessageLookupByLibrary.simpleMessage("Update"),
"upload": MessageLookupByLibrary.simpleMessage("Upload"),

View File

@@ -41,6 +41,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("开启后部分应用可绕过VPN"),
"allowLan": MessageLookupByLibrary.simpleMessage("局域网代理"),
"allowLanDesc": MessageLookupByLibrary.simpleMessage("允许通过局域网访问代理"),
"app": MessageLookupByLibrary.simpleMessage("应用"),
"appAccessControl": MessageLookupByLibrary.simpleMessage("应用访问控制"),
"application": MessageLookupByLibrary.simpleMessage("应用程序"),
"applicationDesc": MessageLookupByLibrary.simpleMessage("修改应用程序相关设置"),
@@ -98,12 +99,16 @@ class MessageLookup extends MessageLookupByLibrary {
"exit": MessageLookupByLibrary.simpleMessage("退出"),
"externalController": MessageLookupByLibrary.simpleMessage("外部控制器"),
"externalControllerDesc":
MessageLookupByLibrary.simpleMessage("开启后通过9090端口控制clash内核"),
MessageLookupByLibrary.simpleMessage("开启后将可以通过9090端口控制clash内核"),
"externalResources": MessageLookupByLibrary.simpleMessage("外部资源"),
"file": MessageLookupByLibrary.simpleMessage("文件"),
"fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"),
"filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"),
"general": MessageLookupByLibrary.simpleMessage("基础"),
"geoData": MessageLookupByLibrary.simpleMessage("地理数据"),
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"),
"geodataLoaderDesc":
MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"),
"global": MessageLookupByLibrary.simpleMessage("全局"),
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
"hours": MessageLookupByLibrary.simpleMessage("小时"),
@@ -167,6 +172,7 @@ class MessageLookup extends MessageLookupByLibrary {
"project": MessageLookupByLibrary.simpleMessage("项目"),
"proxies": MessageLookupByLibrary.simpleMessage("代理"),
"proxyPort": MessageLookupByLibrary.simpleMessage("代理端口"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage("设置clash监听端口"),
"qrcode": MessageLookupByLibrary.simpleMessage("二维码"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage("扫描二维码获取配置文件"),
"recovery": MessageLookupByLibrary.simpleMessage("恢复"),
@@ -189,9 +195,13 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."),
"submit": MessageLookupByLibrary.simpleMessage("提交"),
"systemProxy": MessageLookupByLibrary.simpleMessage("系统代理"),
"systemProxyDesc":
MessageLookupByLibrary.simpleMessage("为VpnService附加HTTP代理"),
"tabAnimation": MessageLookupByLibrary.simpleMessage("选项卡动画"),
"tabAnimationDesc":
MessageLookupByLibrary.simpleMessage("开启后,主页选项卡将添加切换动画"),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许tcp并发"),
"theme": MessageLookupByLibrary.simpleMessage("主题"),
"themeColor": MessageLookupByLibrary.simpleMessage("主题色彩"),
"themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"),
@@ -203,6 +213,8 @@ class MessageLookup extends MessageLookupByLibrary {
"tunDesc": MessageLookupByLibrary.simpleMessage("仅在管理员模式生效"),
"unableToUpdateCurrentProfileDesc":
MessageLookupByLibrary.simpleMessage("无法更新当前配置文件"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage("统一延迟"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage("去除握手等额外延迟"),
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
"update": MessageLookupByLibrary.simpleMessage("更新"),
"upload": MessageLookupByLibrary.simpleMessage("上传"),

View File

@@ -1170,6 +1170,16 @@ class AppLocalizations {
);
}
/// `Set the clash listening port`
String get proxyPortDesc {
return Intl.message(
'Set the clash listening port',
name: 'proxyPortDesc',
desc: '',
args: [],
);
}
/// `Port`
String get port {
return Intl.message(
@@ -1690,10 +1700,10 @@ class AppLocalizations {
);
}
/// `Enabled to some applications can bypass VPN`
/// `Some apps can bypass VPN when turned on`
String get allowBypassDesc {
return Intl.message(
'Enabled to some applications can bypass VPN',
'Some apps can bypass VPN when turned on',
name: 'allowBypassDesc',
desc: '',
args: [],
@@ -1710,25 +1720,115 @@ class AppLocalizations {
);
}
/// `Enabled to control the clash on port 9090`
/// `Once enabled, the clash kernel can be controlled on port 9090`
String get externalControllerDesc {
return Intl.message(
'Enabled to control the clash on port 9090',
'Once enabled, the clash kernel can be controlled on port 9090',
name: 'externalControllerDesc',
desc: '',
args: [],
);
}
/// `Enabled to will allow it to receive ipv6 traffic`
/// `When turned on it will be able to receive ipv6 traffic`
String get ipv6Desc {
return Intl.message(
'Enabled to will allow it to receive ipv6 traffic',
'When turned on it will be able to receive ipv6 traffic',
name: 'ipv6Desc',
desc: '',
args: [],
);
}
/// `App`
String get app {
return Intl.message(
'App',
name: 'app',
desc: '',
args: [],
);
}
/// `General`
String get general {
return Intl.message(
'General',
name: 'general',
desc: '',
args: [],
);
}
/// `Attach HTTP proxy to VpnService`
String get systemProxyDesc {
return Intl.message(
'Attach HTTP proxy to VpnService',
name: 'systemProxyDesc',
desc: '',
args: [],
);
}
/// `Unified delay`
String get unifiedDelay {
return Intl.message(
'Unified delay',
name: 'unifiedDelay',
desc: '',
args: [],
);
}
/// `Remove extra delays such as handshaking`
String get unifiedDelayDesc {
return Intl.message(
'Remove extra delays such as handshaking',
name: 'unifiedDelayDesc',
desc: '',
args: [],
);
}
/// `Tcp concurrent`
String get tcpConcurrent {
return Intl.message(
'Tcp concurrent',
name: 'tcpConcurrent',
desc: '',
args: [],
);
}
/// `Enabling it will allow tcp concurrency`
String get tcpConcurrentDesc {
return Intl.message(
'Enabling it will allow tcp concurrency',
name: 'tcpConcurrentDesc',
desc: '',
args: [],
);
}
/// `Geo Low Memory Mode`
String get geodataLoader {
return Intl.message(
'Geo Low Memory Mode',
name: 'geodataLoader',
desc: '',
args: [],
);
}
/// `Enabling will use the Geo low memory loader`
String get geodataLoaderDesc {
return Intl.message(
'Enabling will use the Geo low memory loader',
name: 'geodataLoaderDesc',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -109,9 +109,12 @@ class ClashConfig extends ChangeNotifier {
int _mixedPort;
bool _allowLan;
bool _ipv6;
String _geodataLoader;
LogLevel _logLevel;
String _externalController;
Mode _mode;
LogLevel _logLevel;
bool _unifiedDelay;
bool _tcpConcurrent;
Tun _tun;
Dns _dns;
List<String> _rules;
@@ -123,15 +126,21 @@ class ClashConfig extends ChangeNotifier {
bool? ipv6,
LogLevel? logLevel,
String? externalController,
String? geodataLoader,
bool? unifiedDelay,
Tun? tun,
Dns? dns,
bool? tcpConcurrent,
List<String>? rules,
}) : _mixedPort = mixedPort ?? 7890,
_mode = mode ?? Mode.rule,
_ipv6 = ipv6 ?? false,
_allowLan = allowLan ?? false,
_tcpConcurrent = tcpConcurrent ?? false,
_logLevel = logLevel ?? LogLevel.info,
_tun = tun ?? const Tun(),
_unifiedDelay = unifiedDelay ?? false,
_geodataLoader = geodataLoader ?? geodataLoaderMemconservative,
_externalController = externalController ?? '',
_dns = dns ?? Dns(),
_rules = rules ?? [];
@@ -195,6 +204,36 @@ class ClashConfig extends ChangeNotifier {
}
}
@JsonKey(name: "geodata-loader", defaultValue: geodataLoaderMemconservative)
String get geodataLoader => _geodataLoader;
set geodataLoader(String value) {
if (_geodataLoader != value) {
_geodataLoader = value;
notifyListeners();
}
}
@JsonKey(name: "unified-delay", defaultValue: false)
bool get unifiedDelay => _unifiedDelay;
set unifiedDelay(bool value) {
if (_unifiedDelay != value) {
_unifiedDelay = value;
notifyListeners();
}
}
@JsonKey(name: "tcp-concurrent", defaultValue: false)
bool get tcpConcurrent => _tcpConcurrent;
set tcpConcurrent(bool value) {
if (_tcpConcurrent != value) {
_tcpConcurrent = value;
notifyListeners();
}
}
Tun get tun => _tun;
set tun(Tun value) {

View File

@@ -29,6 +29,7 @@ class Props with _$Props {
const factory Props({
AccessControl? accessControl,
bool? allowBypass,
bool? systemProxy,
}) = _Props;
factory Props.fromJson(Map<String, Object?> json) =>
@@ -54,6 +55,7 @@ class Config extends ChangeNotifier {
bool _isAnimateToPage;
bool _autoCheckUpdate;
bool _allowBypass;
bool _systemProxy;
DAV? _dav;
Config()
@@ -69,6 +71,7 @@ class Config extends ChangeNotifier {
_isMinimizeOnExit = true,
_isAccessControl = false,
_autoCheckUpdate = true,
_systemProxy = false,
_accessControl = const AccessControl(),
_isAnimateToPage = true,
_allowBypass = true;
@@ -331,6 +334,18 @@ class Config extends ChangeNotifier {
}
}
@JsonKey(defaultValue: true)
bool get systemProxy {
return _systemProxy;
}
set systemProxy(bool value) {
if (_systemProxy != value) {
_systemProxy = value;
notifyListeners();
}
}
update([
Config? config,
RecoveryOption recoveryOptions = RecoveryOption.all,

View File

@@ -39,14 +39,18 @@ ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => ClashConfig(
mixedPort: (json['mixed-port'] as num?)?.toInt(),
mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']),
allowLan: json['allow-lan'] as bool?,
ipv6: json['ipv6'] as bool? ?? false,
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['log-level']),
externalController: json['external-controller'] as String? ?? '',
geodataLoader: json['geodata-loader'] as String? ?? 'memconservative',
unifiedDelay: json['unified-delay'] as bool? ?? false,
tun: json['tun'] == null
? null
: Tun.fromJson(json['tun'] as Map<String, dynamic>),
dns: json['dns'] == null
? null
: Dns.fromJson(json['dns'] as Map<String, dynamic>),
tcpConcurrent: json['tcp-concurrent'] as bool? ?? false,
rules:
(json['rules'] as List<dynamic>?)?.map((e) => e as String).toList(),
);
@@ -58,6 +62,10 @@ Map<String, dynamic> _$ClashConfigToJson(ClashConfig instance) =>
'allow-lan': instance.allowLan,
'log-level': _$LogLevelEnumMap[instance.logLevel]!,
'external-controller': instance.externalController,
'ipv6': instance.ipv6,
'geodata-loader': instance.geodataLoader,
'unified-delay': instance.unifiedDelay,
'tcp-concurrent': instance.tcpConcurrent,
'tun': instance.tun,
'dns': instance.dns,
'rules': instance.rules,

View File

@@ -248,6 +248,7 @@ Props _$PropsFromJson(Map<String, dynamic> json) {
mixin _$Props {
AccessControl? get accessControl => throw _privateConstructorUsedError;
bool? get allowBypass => throw _privateConstructorUsedError;
bool? get systemProxy => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@@ -259,7 +260,8 @@ abstract class $PropsCopyWith<$Res> {
factory $PropsCopyWith(Props value, $Res Function(Props) then) =
_$PropsCopyWithImpl<$Res, Props>;
@useResult
$Res call({AccessControl? accessControl, bool? allowBypass});
$Res call(
{AccessControl? accessControl, bool? allowBypass, bool? systemProxy});
$AccessControlCopyWith<$Res>? get accessControl;
}
@@ -279,6 +281,7 @@ class _$PropsCopyWithImpl<$Res, $Val extends Props>
$Res call({
Object? accessControl = freezed,
Object? allowBypass = freezed,
Object? systemProxy = freezed,
}) {
return _then(_value.copyWith(
accessControl: freezed == accessControl
@@ -289,6 +292,10 @@ class _$PropsCopyWithImpl<$Res, $Val extends Props>
? _value.allowBypass
: allowBypass // ignore: cast_nullable_to_non_nullable
as bool?,
systemProxy: freezed == systemProxy
? _value.systemProxy
: systemProxy // ignore: cast_nullable_to_non_nullable
as bool?,
) as $Val);
}
@@ -312,7 +319,8 @@ abstract class _$$PropsImplCopyWith<$Res> implements $PropsCopyWith<$Res> {
__$$PropsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({AccessControl? accessControl, bool? allowBypass});
$Res call(
{AccessControl? accessControl, bool? allowBypass, bool? systemProxy});
@override
$AccessControlCopyWith<$Res>? get accessControl;
@@ -331,6 +339,7 @@ class __$$PropsImplCopyWithImpl<$Res>
$Res call({
Object? accessControl = freezed,
Object? allowBypass = freezed,
Object? systemProxy = freezed,
}) {
return _then(_$PropsImpl(
accessControl: freezed == accessControl
@@ -341,6 +350,10 @@ class __$$PropsImplCopyWithImpl<$Res>
? _value.allowBypass
: allowBypass // ignore: cast_nullable_to_non_nullable
as bool?,
systemProxy: freezed == systemProxy
? _value.systemProxy
: systemProxy // ignore: cast_nullable_to_non_nullable
as bool?,
));
}
}
@@ -348,7 +361,7 @@ class __$$PropsImplCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$PropsImpl implements _Props {
const _$PropsImpl({this.accessControl, this.allowBypass});
const _$PropsImpl({this.accessControl, this.allowBypass, this.systemProxy});
factory _$PropsImpl.fromJson(Map<String, dynamic> json) =>
_$$PropsImplFromJson(json);
@@ -357,10 +370,12 @@ class _$PropsImpl implements _Props {
final AccessControl? accessControl;
@override
final bool? allowBypass;
@override
final bool? systemProxy;
@override
String toString() {
return 'Props(accessControl: $accessControl, allowBypass: $allowBypass)';
return 'Props(accessControl: $accessControl, allowBypass: $allowBypass, systemProxy: $systemProxy)';
}
@override
@@ -371,12 +386,15 @@ class _$PropsImpl implements _Props {
(identical(other.accessControl, accessControl) ||
other.accessControl == accessControl) &&
(identical(other.allowBypass, allowBypass) ||
other.allowBypass == allowBypass));
other.allowBypass == allowBypass) &&
(identical(other.systemProxy, systemProxy) ||
other.systemProxy == systemProxy));
}
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(runtimeType, accessControl, allowBypass);
int get hashCode =>
Object.hash(runtimeType, accessControl, allowBypass, systemProxy);
@JsonKey(ignore: true)
@override
@@ -395,7 +413,8 @@ class _$PropsImpl implements _Props {
abstract class _Props implements Props {
const factory _Props(
{final AccessControl? accessControl,
final bool? allowBypass}) = _$PropsImpl;
final bool? allowBypass,
final bool? systemProxy}) = _$PropsImpl;
factory _Props.fromJson(Map<String, dynamic> json) = _$PropsImpl.fromJson;
@@ -404,6 +423,8 @@ abstract class _Props implements Props {
@override
bool? get allowBypass;
@override
bool? get systemProxy;
@override
@JsonKey(ignore: true)
_$$PropsImplCopyWith<_$PropsImpl> get copyWith =>
throw _privateConstructorUsedError;

View File

@@ -33,7 +33,8 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true
..isCompatible = json['isCompatible'] as bool? ?? true
..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true
..allowBypass = json['allowBypass'] as bool? ?? true;
..allowBypass = json['allowBypass'] as bool? ?? true
..systemProxy = json['systemProxy'] as bool? ?? true;
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
'profiles': instance.profiles,
@@ -54,6 +55,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
'isCompatible': instance.isCompatible,
'autoCheckUpdate': instance.autoCheckUpdate,
'allowBypass': instance.allowBypass,
'systemProxy': instance.systemProxy,
};
const _$ThemeModeEnumMap = {
@@ -102,10 +104,12 @@ _$PropsImpl _$$PropsImplFromJson(Map<String, dynamic> json) => _$PropsImpl(
: AccessControl.fromJson(
json['accessControl'] as Map<String, dynamic>),
allowBypass: json['allowBypass'] as bool?,
systemProxy: json['systemProxy'] as bool?,
);
Map<String, dynamic> _$$PropsImplToJson(_$PropsImpl instance) =>
<String, dynamic>{
'accessControl': instance.accessControl,
'allowBypass': instance.allowBypass,
'systemProxy': instance.systemProxy,
};

View File

@@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -17,10 +18,8 @@ class App {
methodChannel = const MethodChannel("app");
methodChannel!.setMethodCallHandler((call) async {
switch (call.method) {
case "exit":
if (onExit != null) {
await onExit!();
}
case "gc":
clashCore.requestGc();
break;
default:
throw MissingPluginException();
@@ -29,10 +28,6 @@ class App {
}
}
setOnExit(Function() onExit) {
this.onExit = onExit;
}
factory App() {
_instance ??= App._internal();
return _instance!;

View File

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