From 8d4931c09365b47b2d799f3cbf705b9c697609c7 Mon Sep 17 00:00:00 2001 From: chen08209 Date: Thu, 26 Sep 2024 14:29:04 +0800 Subject: [PATCH] Fix windows admin auto launch issues Add android vpn options Support proxies icon configuration Optimize android immersion display Fix some issues --- .../com/follow/clash/BaseServiceInterface.kt | 6 +- .../kotlin/com/follow/clash/extensions/Ext.kt | 20 + .../kotlin/com/follow/clash/models/Props.kt | 28 +- .../com/follow/clash/plugins/VpnPlugin.kt | 20 +- .../follow/clash/services/FlClashService.kt | 5 +- .../clash/services/FlClashVpnService.kt | 87 +- core/common.go | 301 ++-- core/hub.go | 9 +- core/state.go | 48 +- core/state/state.go | 59 + core/tun.go | 22 +- core/tun/tun.go | 21 +- lib/application.dart | 3 +- lib/clash/core.dart | 25 +- lib/clash/generated/clash_ffi.dart | 64 +- lib/common/constant.dart | 18 +- lib/common/launch.dart | 21 +- lib/common/measure.dart | 24 +- lib/common/path.dart | 20 +- lib/common/preferences.dart | 7 +- lib/common/request.dart | 3 +- lib/common/scroll.dart | 15 + lib/common/string.dart | 39 + lib/common/system.dart | 1 - lib/common/windows.dart | 58 + lib/controller.dart | 31 +- lib/enum/enum.dart | 8 +- lib/fragments/application_setting.dart | 153 +- lib/fragments/config/app.dart | 61 - lib/fragments/config/config.dart | 37 +- lib/fragments/config/dns.dart | 166 +-- lib/fragments/config/general.dart | 18 +- lib/fragments/config/network.dart | 270 ++++ lib/fragments/config/vpn.dart | 169 --- lib/fragments/hotkey.dart | 2 +- lib/fragments/logs.dart | 80 +- lib/fragments/profiles/profiles.dart | 5 +- lib/fragments/profiles/view_profile.dart | 28 +- lib/fragments/proxies/common.dart | 4 +- lib/fragments/proxies/list.dart | 242 ++-- lib/fragments/proxies/proxies.dart | 67 +- lib/fragments/proxies/setting.dart | 87 +- lib/fragments/proxies/tab.dart | 61 +- lib/fragments/requests.dart | 4 +- lib/fragments/tools.dart | 8 +- lib/l10n/arb/intl_en.arb | 24 +- lib/l10n/arb/intl_zh_CN.arb | 22 +- lib/l10n/intl/messages_en.dart | 30 +- lib/l10n/intl/messages_zh_CN.dart | 21 +- lib/l10n/l10n.dart | 192 ++- lib/main.dart | 8 +- lib/manager/android_manager.dart | 26 +- lib/manager/app_state_manager.dart | 2 +- lib/manager/clash_manager.dart | 14 +- lib/manager/hotkey_manager.dart | 3 +- lib/manager/media_manager.dart | 27 +- lib/manager/tray_manager.dart | 13 +- lib/manager/vpn_manager.dart | 11 +- lib/manager/window_manager.dart | 8 +- lib/models/app.dart | 29 +- lib/models/clash_config.dart | 27 +- lib/models/common.dart | 13 +- lib/models/config.dart | 420 ++---- lib/models/ffi.dart | 35 + lib/models/generated/common.freezed.dart | 150 ++ lib/models/generated/config.freezed.dart | 1245 +++++++++-------- lib/models/generated/config.g.dart | 223 ++- lib/models/generated/ffi.freezed.dart | 660 +++++++++ lib/models/generated/ffi.g.dart | 62 + lib/models/generated/selector.freezed.dart | 236 ++-- lib/models/selector.dart | 20 +- lib/pages/home.dart | 15 +- lib/plugins/vpn.dart | 17 +- lib/state.dart | 37 +- lib/widgets/builder.dart | 25 +- lib/widgets/icon.dart | 52 + lib/widgets/input.dart | 240 +++- lib/widgets/scaffold.dart | 16 + lib/widgets/widgets.dart | 1 + pubspec.yaml | 3 +- 80 files changed, 3910 insertions(+), 2442 deletions(-) create mode 100644 core/state/state.go delete mode 100644 lib/fragments/config/app.dart create mode 100644 lib/fragments/config/network.dart delete mode 100644 lib/fragments/config/vpn.dart create mode 100644 lib/widgets/icon.dart diff --git a/android/app/src/main/kotlin/com/follow/clash/BaseServiceInterface.kt b/android/app/src/main/kotlin/com/follow/clash/BaseServiceInterface.kt index 85176e78..96f64082 100644 --- a/android/app/src/main/kotlin/com/follow/clash/BaseServiceInterface.kt +++ b/android/app/src/main/kotlin/com/follow/clash/BaseServiceInterface.kt @@ -1,10 +1,10 @@ package com.follow.clash -import com.follow.clash.models.Props -import com.follow.clash.models.TunProps + +import com.follow.clash.models.VpnOptions interface BaseServiceInterface { - fun start(port: Int, props: Props?): TunProps? + fun start(options: VpnOptions): Int fun stop() fun startForeground(title: String, content: String) } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt b/android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt index 0ef55f0c..4e30de2e 100644 --- a/android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt +++ b/android/app/src/main/kotlin/com/follow/clash/extensions/Ext.kt @@ -8,6 +8,7 @@ import android.system.OsConstants.IPPROTO_TCP import android.system.OsConstants.IPPROTO_UDP import android.util.Base64 import androidx.core.graphics.drawable.toBitmap +import com.follow.clash.models.CIDR import com.follow.clash.models.Metadata import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -33,6 +34,25 @@ fun Metadata.getProtocol(): Int? { return null } +fun String.toCIDR(): CIDR { + val parts = split("/") + if (parts.size != 2) { + throw IllegalArgumentException("Invalid CIDR format") + } + val ipAddress = parts[0] + val prefixLength = parts[1].toIntOrNull() + ?: throw IllegalArgumentException("Invalid prefix length") + + val address = InetAddress.getByName(ipAddress) + + val maxPrefix = if (address.address.size == 4) 32 else 128 + if (prefixLength < 0 || prefixLength > maxPrefix) { + throw IllegalArgumentException("Invalid prefix length for IP version") + } + + return CIDR(address, prefixLength) +} + fun ConnectivityManager.resolveDns(network: Network?): List { val properties = getLinkProperties(network) ?: return listOf() diff --git a/android/app/src/main/kotlin/com/follow/clash/models/Props.kt b/android/app/src/main/kotlin/com/follow/clash/models/Props.kt index 5a3287de..b40a7b60 100644 --- a/android/app/src/main/kotlin/com/follow/clash/models/Props.kt +++ b/android/app/src/main/kotlin/com/follow/clash/models/Props.kt @@ -1,5 +1,7 @@ package com.follow.clash.models +import java.net.InetAddress + enum class AccessControlMode { acceptSelected, rejectSelected, @@ -11,20 +13,16 @@ data class AccessControl( val rejectList: List, ) -data class Props( - val enable: Boolean?, - val accessControl: AccessControl?, - val allowBypass: Boolean?, - val systemProxy: Boolean?, - val ipv6: Boolean?, -) +data class CIDR(val address: InetAddress, val prefixLength: Int) -data class TunProps( - val fd: Int, - val gateway: String, - val gateway6: String, - val portal: String, - val portal6: String, - val dns: String, - val dns6: String +data class VpnOptions( + val enable: Boolean, + val port: Int, + val accessControl: AccessControl?, + val allowBypass: Boolean, + val systemProxy: Boolean, + val bypassDomain: List, + val ipv4Address: String, + val ipv6Address: String, + val dnsServerAddress: String, ) \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt b/android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt index 01bb6f78..4b443382 100644 --- a/android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt +++ b/android/app/src/main/kotlin/com/follow/clash/plugins/VpnPlugin.kt @@ -16,8 +16,6 @@ import com.follow.clash.GlobalState import com.follow.clash.RunState import com.follow.clash.extensions.getProtocol import com.follow.clash.extensions.resolveDns -import com.follow.clash.models.Props -import com.follow.clash.models.TunProps import com.follow.clash.services.FlClashService import com.follow.clash.services.FlClashVpnService import com.google.gson.Gson @@ -31,14 +29,14 @@ import kotlinx.coroutines.withContext import java.net.InetSocketAddress import kotlin.concurrent.withLock import com.follow.clash.models.Process +import com.follow.clash.models.VpnOptions class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { private lateinit var flutterMethodChannel: MethodChannel private lateinit var context: Context private var flClashService: BaseServiceInterface? = null - private var port: Int = 7890 - private var props: Props? = null + private lateinit var options: VpnOptions private lateinit var scope: CoroutineScope private val connectivity by lazy { @@ -78,11 +76,9 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "start" -> { - port = call.argument("port")!! - val args = call.argument("args") - props = - if (args != null) Gson().fromJson(args, Props::class.java) else null - when (props?.enable == true) { + val data = call.argument("data") + options = Gson().fromJson(data, VpnOptions::class.java) + when (options.enable) { true -> handleStartVpn() false -> start() } @@ -241,10 +237,10 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { GlobalState.runLock.withLock { if (GlobalState.runState.value == RunState.START) return GlobalState.runState.value = RunState.START - val tunProps = flClashService?.start(port, props) + val fd = flClashService?.start(options) flutterMethodChannel.invokeMethod( "started", - Gson().toJson(tunProps, TunProps::class.java) + fd ) } } @@ -259,7 +255,7 @@ class VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { } private fun bindService() { - val intent = when (props?.enable == true) { + val intent = when (options.enable) { true -> Intent(context, FlClashVpnService::class.java) false -> Intent(context, FlClashService::class.java) } diff --git a/android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt b/android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt index 09a8c614..67e4bd59 100644 --- a/android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt +++ b/android/app/src/main/kotlin/com/follow/clash/services/FlClashService.kt @@ -14,8 +14,7 @@ import android.os.IBinder import androidx.core.app.NotificationCompat import com.follow.clash.BaseServiceInterface import com.follow.clash.MainActivity -import com.follow.clash.models.Props - +import com.follow.clash.models.VpnOptions class FlClashService : Service(), BaseServiceInterface { @@ -72,7 +71,7 @@ class FlClashService : Service(), BaseServiceInterface { } } - override fun start(port: Int, props: Props?) = null + override fun start(options: VpnOptions) = 0 override fun stop() { stopSelf() diff --git a/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt b/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt index 8b38aa7f..79f69000 100644 --- a/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt +++ b/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt @@ -21,72 +21,35 @@ import com.follow.clash.GlobalState import com.follow.clash.MainActivity import com.follow.clash.R import com.follow.clash.TempActivity +import com.follow.clash.extensions.toCIDR import com.follow.clash.models.AccessControlMode -import com.follow.clash.models.Props -import com.follow.clash.models.TunProps +import com.follow.clash.models.VpnOptions import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch class FlClashVpnService : VpnService(), BaseServiceInterface { - - companion object { - private val passList = listOf( - "*zhihu.com", - "*zhimg.com", - "*jd.com", - "100ime-iat-api.xfyun.cn", - "*360buyimg.com", - "localhost", - "*.local", - "127.*", - "10.*", - "172.16.*", - "172.17.*", - "172.18.*", - "172.19.*", - "172.2*", - "172.30.*", - "172.31.*", - "192.168.*" - ) - private const val TUN_MTU = 9000 - private const val TUN_SUBNET_PREFIX = 30 - private const val TUN_GATEWAY = "172.19.0.1" - private const val TUN_SUBNET_PREFIX6 = 126 - private const val TUN_GATEWAY6 = "fdfe:dcba:9876::1" - private const val TUN_PORTAL = "172.19.0.2" - private const val TUN_PORTAL6 = "fdfe:dcba:9876::2" - private const val TUN_DNS = TUN_PORTAL - private const val TUN_DNS6 = TUN_PORTAL6 - private const val NET_ANY = "0.0.0.0" - private const val NET_ANY6 = "::" - } - override fun onCreate() { super.onCreate() GlobalState.initServiceEngine(applicationContext) } - override fun start(port: Int, props: Props?): TunProps { + override fun start(options: VpnOptions): Int { return with(Builder()) { - addAddress(TUN_GATEWAY, TUN_SUBNET_PREFIX) - addRoute(NET_ANY, 0) - addDnsServer(TUN_DNS) - - - if (props?.ipv6 == true) { - try { - addAddress(TUN_GATEWAY6, TUN_SUBNET_PREFIX6) - addRoute(NET_ANY6, 0) - addDnsServer(TUN_DNS6) - } catch (_: Exception) { - - } + if (options.ipv4Address.isNotEmpty()) { + val cidr = options.ipv4Address.toCIDR() + addAddress(cidr.address, cidr.prefixLength) + addRoute("0.0.0.0", 0) } - setMtu(TUN_MTU) - props?.accessControl?.let { accessControl -> + if (options.ipv6Address.isNotEmpty()) { + val cidr = options.ipv6Address.toCIDR() + addAddress(cidr.address, cidr.prefixLength) + addRoute("::", 0) + } + addDnsServer(options.dnsServerAddress) + setMtu(9000) + options.accessControl?.let { accessControl -> when (accessControl.mode) { AccessControlMode.acceptSelected -> { (accessControl.acceptList + packageName).forEach { @@ -106,28 +69,20 @@ class FlClashVpnService : VpnService(), BaseServiceInterface { if (Build.VERSION.SDK_INT >= 29) { setMetered(false) } - if (props?.allowBypass == true) { + if (options.allowBypass) { allowBypass() } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && props?.systemProxy == true) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.systemProxy) { setHttpProxy( ProxyInfo.buildDirectProxy( "127.0.0.1", - port, - passList + options.port, + options.bypassDomain ) ) } - TunProps( - fd = establish()?.detachFd() - ?: throw NullPointerException("Establish VPN rejected by system"), - gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX", - gateway6 = if (props?.ipv6 == true) "$TUN_GATEWAY6/$TUN_SUBNET_PREFIX6" else "", - portal = TUN_PORTAL, - portal6 = if (props?.ipv6 == true) TUN_PORTAL6 else "", - dns = TUN_DNS, - dns6 = if (props?.ipv6 == true) TUN_DNS6 else "" - ) + establish()?.detachFd() + ?: throw NullPointerException("Establish VPN rejected by system") } } diff --git a/core/common.go b/core/common.go index e767987d..cc0d117f 100644 --- a/core/common.go +++ b/core/common.go @@ -3,14 +3,15 @@ package main import "C" import ( "context" + "core/state" "errors" - route "github.com/metacubex/mihomo/hub/route" + "github.com/metacubex/mihomo/constant/features" + "github.com/metacubex/mihomo/hub/route" "math" "os" "os/exec" "path/filepath" "runtime" - "strings" "sync" "syscall" "time" @@ -234,151 +235,151 @@ func decorationConfig(profileId string, cfg config.RawConfig) *config.RawConfig return prof } -func Reduce[T any, U any](s []T, initVal U, f func(U, T) U) U { - for _, v := range s { - initVal = f(initVal, v) - } - return initVal -} - -func Map[T, U any](slice []T, fn func(T) U) []U { - result := make([]U, len(slice)) - for i, v := range slice { - result[i] = fn(v) - } - return result -} - -func replaceFromMap(s string, m map[string]string) string { - for k, v := range m { - s = strings.ReplaceAll(s, k, v) - } - return s -} - -func removeDuplicateFromSlice[T any](slice []T) []T { - result := make([]T, 0) - seen := make(map[any]struct{}) - for _, value := range slice { - if _, ok := seen[value]; !ok { - result = append(result, value) - seen[value] = struct{}{} - } - } - return result -} - -func generateProxyGroupAndRule(proxyGroup *[]map[string]any, rule *[]string) { - var replacements = map[string]string{} - var selectArr []map[string]any - var urlTestArr []map[string]any - var fallbackArr []map[string]any - for _, group := range *proxyGroup { - switch group["type"] { - case "select": - selectArr = append(selectArr, group) - replacements[group["name"].(string)] = "Proxy" - break - case "url-test": - urlTestArr = append(urlTestArr, group) - replacements[group["name"].(string)] = "Auto" - break - case "fallback": - fallbackArr = append(fallbackArr, group) - replacements[group["name"].(string)] = "Fallback" - break - default: - break - } - } - - ProxyProxies := Reduce(selectArr, []string{}, func(res []string, cur map[string]any) []string { - if cur["proxies"] == nil { - return res - } - for _, proxyName := range cur["proxies"].([]interface{}) { - if str, ok := proxyName.(string); ok { - str = replaceFromMap(str, replacements) - if str != "Proxy" { - res = append(res, str) - } - } - } - return res - }) - - ProxyProxies = removeDuplicateFromSlice(ProxyProxies) - - AutoProxies := Reduce(urlTestArr, []string{}, func(res []string, cur map[string]any) []string { - if cur["proxies"] == nil { - return res - } - for _, proxyName := range cur["proxies"].([]interface{}) { - if str, ok := proxyName.(string); ok { - str = replaceFromMap(str, replacements) - if str != "Auto" { - res = append(res, str) - } - } - } - return res - }) - - AutoProxies = removeDuplicateFromSlice(AutoProxies) - - FallbackProxies := Reduce(fallbackArr, []string{}, func(res []string, cur map[string]any) []string { - if cur["proxies"] == nil { - return res - } - for _, proxyName := range cur["proxies"].([]interface{}) { - if str, ok := proxyName.(string); ok { - str = replaceFromMap(str, replacements) - if str != "Fallback" { - res = append(res, str) - } - } - } - return res - }) - - FallbackProxies = removeDuplicateFromSlice(FallbackProxies) - - var computedProxyGroup []map[string]any - - if len(ProxyProxies) > 0 { - computedProxyGroup = append(computedProxyGroup, - map[string]any{ - "name": "Proxy", - "type": "select", - "proxies": ProxyProxies, - }) - } - - if len(AutoProxies) > 0 { - computedProxyGroup = append(computedProxyGroup, - map[string]any{ - "name": "Auto", - "type": "url-test", - "proxies": AutoProxies, - }) - } - - if len(FallbackProxies) > 0 { - computedProxyGroup = append(computedProxyGroup, - map[string]any{ - "name": "Fallback", - "type": "fallback", - "proxies": FallbackProxies, - }) - } - - computedRule := Map(*rule, func(value string) string { - return replaceFromMap(value, replacements) - }) - - *proxyGroup = computedProxyGroup - *rule = computedRule -} +//func Reduce[T any, U any](s []T, initVal U, f func(U, T) U) U { +// for _, v := range s { +// initVal = f(initVal, v) +// } +// return initVal +//} +// +//func Map[T, U any](slice []T, fn func(T) U) []U { +// result := make([]U, len(slice)) +// for i, v := range slice { +// result[i] = fn(v) +// } +// return result +//} +// +//func replaceFromMap(s string, m map[string]string) string { +// for k, v := range m { +// s = strings.ReplaceAll(s, k, v) +// } +// return s +//} +// +//func removeDuplicateFromSlice[T any](slice []T) []T { +// result := make([]T, 0) +// seen := make(map[any]struct{}) +// for _, value := range slice { +// if _, ok := seen[value]; !ok { +// result = append(result, value) +// seen[value] = struct{}{} +// } +// } +// return result +//} + +//func generateProxyGroupAndRule(proxyGroup *[]map[string]any, rule *[]string) { +// var replacements = map[string]string{} +// var selectArr []map[string]any +// var urlTestArr []map[string]any +// var fallbackArr []map[string]any +// for _, group := range *proxyGroup { +// switch group["type"] { +// case "select": +// selectArr = append(selectArr, group) +// replacements[group["name"].(string)] = "Proxy" +// break +// case "url-test": +// urlTestArr = append(urlTestArr, group) +// replacements[group["name"].(string)] = "Auto" +// break +// case "fallback": +// fallbackArr = append(fallbackArr, group) +// replacements[group["name"].(string)] = "Fallback" +// break +// default: +// break +// } +// } +// +// ProxyProxies := Reduce(selectArr, []string{}, func(res []string, cur map[string]any) []string { +// if cur["proxies"] == nil { +// return res +// } +// for _, proxyName := range cur["proxies"].([]interface{}) { +// if str, ok := proxyName.(string); ok { +// str = replaceFromMap(str, replacements) +// if str != "Proxy" { +// res = append(res, str) +// } +// } +// } +// return res +// }) +// +// ProxyProxies = removeDuplicateFromSlice(ProxyProxies) +// +// AutoProxies := Reduce(urlTestArr, []string{}, func(res []string, cur map[string]any) []string { +// if cur["proxies"] == nil { +// return res +// } +// for _, proxyName := range cur["proxies"].([]interface{}) { +// if str, ok := proxyName.(string); ok { +// str = replaceFromMap(str, replacements) +// if str != "Auto" { +// res = append(res, str) +// } +// } +// } +// return res +// }) +// +// AutoProxies = removeDuplicateFromSlice(AutoProxies) +// +// FallbackProxies := Reduce(fallbackArr, []string{}, func(res []string, cur map[string]any) []string { +// if cur["proxies"] == nil { +// return res +// } +// for _, proxyName := range cur["proxies"].([]interface{}) { +// if str, ok := proxyName.(string); ok { +// str = replaceFromMap(str, replacements) +// if str != "Fallback" { +// res = append(res, str) +// } +// } +// } +// return res +// }) +// +// FallbackProxies = removeDuplicateFromSlice(FallbackProxies) +// +// var computedProxyGroup []map[string]any +// +// if len(ProxyProxies) > 0 { +// computedProxyGroup = append(computedProxyGroup, +// map[string]any{ +// "name": "Proxy", +// "type": "select", +// "proxies": ProxyProxies, +// }) +// } +// +// if len(AutoProxies) > 0 { +// computedProxyGroup = append(computedProxyGroup, +// map[string]any{ +// "name": "Auto", +// "type": "url-test", +// "proxies": AutoProxies, +// }) +// } +// +// if len(FallbackProxies) > 0 { +// computedProxyGroup = append(computedProxyGroup, +// map[string]any{ +// "name": "Fallback", +// "type": "fallback", +// "proxies": FallbackProxies, +// }) +// } +// +// computedRule := Map(*rule, func(value string) string { +// return replaceFromMap(value, replacements) +// }) +// +// *proxyGroup = computedProxyGroup +// *rule = computedRule +//} func genHosts(hosts, patchHosts map[string]any) { for k, v := range patchHosts { @@ -468,7 +469,9 @@ func updateListeners(general *config.General, listeners map[string]constant.Inbo listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel) listener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel) listener.ReCreateTuic(general.TuicServer, tunnel.Tunnel) - listener.ReCreateTun(general.Tun, tunnel.Tunnel) + if !features.Android { + listener.ReCreateTun(general.Tun, tunnel.Tunnel) + } } func stopListeners() { @@ -522,7 +525,7 @@ func patchSelectGroup() { } func applyConfig() error { - cfg, err := config.ParseRawConfig(currentRawConfig) + cfg, err := config.ParseRawConfig(state.CurrentRawConfig) if err != nil { cfg, _ = config.ParseRawConfig(config.DefaultRawConfig()) } diff --git a/core/hub.go b/core/hub.go index f3939354..c0ec42c6 100644 --- a/core/hub.go +++ b/core/hub.go @@ -7,6 +7,7 @@ import "C" import ( "context" bridge "core/dart-bridge" + "core/state" "encoding/json" "fmt" "github.com/metacubex/mihomo/common/utils" @@ -30,8 +31,6 @@ import ( "github.com/metacubex/mihomo/tunnel/statistic" ) -var currentRawConfig = config.DefaultRawConfig() - var configParams = ConfigExtendedParams{} var externalProviders = map[string]cp.Provider{} @@ -124,7 +123,7 @@ func updateConfig(s *C.char, port C.longlong) { } configParams = params.Params prof := decorationConfig(params.ProfileId, params.Config) - currentRawConfig = prof + state.CurrentRawConfig = prof err = applyConfig() if err != nil { bridge.SendToPort(i, err.Error()) @@ -184,7 +183,7 @@ func changeProxy(s *C.char) { //export getTraffic func getTraffic() *C.char { - up, down := statistic.DefaultManager.Current(state.OnlyProxy) + up, down := statistic.DefaultManager.Current(state.CurrentState.OnlyProxy) traffic := map[string]int64{ "up": up, "down": down, @@ -199,7 +198,7 @@ func getTraffic() *C.char { //export getTotalTraffic func getTotalTraffic() *C.char { - up, down := statistic.DefaultManager.Total(state.OnlyProxy) + up, down := statistic.DefaultManager.Total(state.CurrentState.OnlyProxy) traffic := map[string]int64{ "up": up, "down": down, diff --git a/core/state.go b/core/state.go index 7eab116d..e39ce351 100644 --- a/core/state.go +++ b/core/state.go @@ -2,37 +2,33 @@ package main import "C" import ( + "core/state" "encoding/json" "fmt" ) -type AccessControl struct { - Mode string `json:"mode"` - AcceptList []string `json:"acceptList"` - RejectList []string `json:"rejectList"` - IsFilterSystemApp bool `json:"isFilterSystemApp"` -} - -type AndroidProps struct { - Enable bool `json:"enable"` - AccessControl *AccessControl `json:"accessControl"` - AllowBypass bool `json:"allowBypass"` - SystemProxy bool `json:"systemProxy"` - Ipv6 bool `json:"ipv6"` -} - -type State struct { - AndroidProps - CurrentProfileName string `json:"currentProfileName"` - MixedPort int `json:"mixedPort"` - OnlyProxy bool `json:"onlyProxy"` +//export getCurrentProfileName +func getCurrentProfileName() *C.char { + if state.CurrentState == nil { + return C.CString("") + } + return C.CString(state.CurrentState.CurrentProfileName) } -var state State - -//export getState -func getState() *C.char { - data, err := json.Marshal(state) +//export getAndroidVpnOptions +func getAndroidVpnOptions() *C.char { + options := state.AndroidVpnOptions{ + Enable: state.CurrentState.Enable, + Port: state.CurrentRawConfig.MixedPort, + Ipv4Address: state.DefaultIpv4Address, + Ipv6Address: state.GetIpv6Address(), + AccessControl: state.CurrentState.AccessControl, + SystemProxy: state.CurrentState.SystemProxy, + AllowBypass: state.CurrentState.AllowBypass, + BypassDomain: state.CurrentState.BypassDomain, + DnsServerAddress: state.GetDnsServerAddress(), + } + data, err := json.Marshal(options) if err != nil { fmt.Println("Error:", err) return C.CString("") @@ -43,7 +39,7 @@ func getState() *C.char { //export setState func setState(s *C.char) { paramsString := C.GoString(s) - err := json.Unmarshal([]byte(paramsString), &state) + err := json.Unmarshal([]byte(paramsString), state.CurrentState) if err != nil { return } diff --git a/core/state/state.go b/core/state/state.go new file mode 100644 index 00000000..adb67874 --- /dev/null +++ b/core/state/state.go @@ -0,0 +1,59 @@ +package state + +import "github.com/metacubex/mihomo/config" + +var DefaultIpv4Address = "172.19.0.1/30" +var DefaultDnsAddress = "172.19.0.2" +var DefaultIpv6Address = "fdfe:dcba:9876::1/126" + +var CurrentRawConfig = config.DefaultRawConfig() + +type AndroidVpnOptions struct { + Enable bool `json:"enable"` + Port int `json:"port"` + AccessControl *AccessControl `json:"accessControl"` + AllowBypass bool `json:"allowBypass"` + SystemProxy bool `json:"systemProxy"` + BypassDomain []string `json:"bypassDomain"` + Ipv4Address string `json:"ipv4Address"` + Ipv6Address string `json:"ipv6Address"` + DnsServerAddress string `json:"dnsServerAddress"` +} + +type AccessControl struct { + Mode string `json:"mode"` + AcceptList []string `json:"acceptList"` + RejectList []string `json:"rejectList"` + IsFilterSystemApp bool `json:"isFilterSystemApp"` +} + +type AndroidVpnRawOptions struct { + Enable bool `json:"enable"` + AccessControl *AccessControl `json:"accessControl"` + AllowBypass bool `json:"allowBypass"` + SystemProxy bool `json:"systemProxy"` + Ipv6 bool `json:"ipv6"` + BypassDomain []string `json:"bypassDomain"` +} + +type State struct { + AndroidVpnRawOptions + CurrentProfileName string `json:"currentProfileName"` + OnlyProxy bool `json:"onlyProxy"` +} + +var CurrentState = &State{} + +func GetIpv6Address() string { + if CurrentState.Ipv6 { + return DefaultIpv6Address + } else { + return "" + } +} + +func GetDnsServerAddress() string { + //prefix, _ := netip.ParsePrefix(DefaultIpv4Address) + //return prefix.Addr().String() + return DefaultDnsAddress +} diff --git a/core/tun.go b/core/tun.go index 8e0656d8..c2e73acd 100644 --- a/core/tun.go +++ b/core/tun.go @@ -6,7 +6,6 @@ import "C" import ( "core/platform" t "core/tun" - "encoding/json" "errors" "github.com/metacubex/mihomo/listener/sing_tun" "strconv" @@ -42,11 +41,10 @@ var ( ) //export startTUN -func startTUN(s *C.char, port C.longlong) { +func startTUN(fd C.int, port C.longlong) { i := int64(port) ServicePort = i - paramsString := C.GoString(s) - if paramsString == "" { + if fd == 0 { tunLock.Lock() defer tunLock.Unlock() now := time.Now() @@ -61,20 +59,8 @@ func startTUN(s *C.char, port C.longlong) { go func() { tunLock.Lock() defer tunLock.Unlock() - - var tunProps = &t.Props{} - err := json.Unmarshal([]byte(paramsString), tunProps) - if err != nil { - log.Errorln("startTUN error: %v", err) - return - } - - tunListener, err = t.Start(*tunProps) - - if err != nil { - return - } - + f := int(fd) + tunListener, _ = t.Start(f) if tunListener != nil { log.Infoln("TUN address: %v", tunListener.Address()) } diff --git a/core/tun/tun.go b/core/tun/tun.go index 19e5ee12..780dd5bb 100644 --- a/core/tun/tun.go +++ b/core/tun/tun.go @@ -4,7 +4,7 @@ package tun import "C" import ( - "github.com/metacubex/mihomo/constant" + "core/state" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/sing_tun" "github.com/metacubex/mihomo/log" @@ -23,17 +23,17 @@ type Props struct { Dns6 string `json:"dns6"` } -func Start(tunProps Props) (*sing_tun.Listener, error) { +func Start(fd int) (*sing_tun.Listener, error) { var prefix4 []netip.Prefix - tempPrefix4, err := netip.ParsePrefix(tunProps.Gateway) + tempPrefix4, err := netip.ParsePrefix(state.DefaultIpv4Address) if err != nil { log.Errorln("startTUN error:", err) return nil, err } prefix4 = append(prefix4, tempPrefix4) var prefix6 []netip.Prefix - if tunProps.Gateway6 != "" { - tempPrefix6, err := netip.ParsePrefix(tunProps.Gateway6) + if state.CurrentState.Ipv6 { + tempPrefix6, err := netip.ParsePrefix(state.DefaultIpv6Address) if err != nil { log.Errorln("startTUN error:", err) return nil, err @@ -42,22 +42,19 @@ func Start(tunProps Props) (*sing_tun.Listener, error) { } var dnsHijack []string - dnsHijack = append(dnsHijack, net.JoinHostPort(tunProps.Dns, "53")) - if tunProps.Dns6 != "" { - dnsHijack = append(dnsHijack, net.JoinHostPort(tunProps.Dns6, "53")) - } + dnsHijack = append(dnsHijack, net.JoinHostPort(state.GetDnsServerAddress(), "53")) options := LC.Tun{ Enable: true, - Device: sing_tun.InterfaceName, - Stack: constant.TunMixed, + Device: state.CurrentRawConfig.Tun.Device, + Stack: state.CurrentRawConfig.Tun.Stack, DNSHijack: dnsHijack, AutoRoute: false, AutoDetectInterface: false, Inet4Address: prefix4, Inet6Address: prefix6, MTU: 9000, - FileDescriptor: tunProps.Fd, + FileDescriptor: fd, } listener, err := sing_tun.New(options, tunnel.Tunnel) diff --git a/lib/application.dart b/lib/application.dart index 6e948515..db20920d 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -34,7 +34,6 @@ runAppWithPreferences( create: (_) => appState, update: (_, config, clashConfig, appState) { appState?.mode = clashConfig.mode; - appState?.isCompatible = config.isCompatible; appState?.selectedMap = config.currentSelectedMap; return appState!; }, @@ -163,7 +162,7 @@ class ApplicationState extends State { child: ClashManager( child: Selector2( selector: (_, appState, config) => ApplicationSelectorState( - locale: config.locale, + locale: config.appSetting.locale, themeMode: config.themeMode, primaryColor: config.primaryColor, prueBlack: config.prueBlack, diff --git a/lib/clash/core.dart b/lib/clash/core.dart index 69b10307..e5dc2875 100644 --- a/lib/clash/core.dart +++ b/lib/clash/core.dart @@ -287,13 +287,18 @@ class ClashCore { malloc.free(stateChar); } - CoreState getState() { - final stateRaw = clashFFI.getState(); - final state = json.decode( - stateRaw.cast().toDartString(), - ); - clashFFI.freeCString(stateRaw); - return CoreState.fromJson(state); + String getCurrentProfileName() { + final currentProfileRaw = clashFFI.getCurrentProfileName(); + final currentProfile = currentProfileRaw.cast().toDartString(); + clashFFI.freeCString(currentProfileRaw); + return currentProfile; + } + + AndroidVpnOptions getAndroidVpnOptions() { + final vpnOptionsRaw = clashFFI.getAndroidVpnOptions(); + final vpnOptions = json.decode(vpnOptionsRaw.cast().toDartString()); + clashFFI.freeCString(vpnOptionsRaw); + return AndroidVpnOptions.fromJson(vpnOptions); } Traffic getTraffic() { @@ -322,11 +327,9 @@ class ClashCore { clashFFI.stopLog(); } - startTun(TunProps? tunProps, int port) { + startTun(int fd, int port) { if (!Platform.isAndroid) return; - final tunPropsChar = json.encode(tunProps).toNativeUtf8().cast(); - clashFFI.startTUN(tunPropsChar, port); - malloc.free(tunPropsChar); + clashFFI.startTUN(fd, port); } updateDns(String dns) { diff --git a/lib/clash/generated/clash_ffi.dart b/lib/clash/generated/clash_ffi.dart index 95ccc9c6..9449c62e 100644 --- a/lib/clash/generated/clash_ffi.dart +++ b/lib/clash/generated/clash_ffi.dart @@ -5144,6 +5144,20 @@ class ClashFFI { late final __FCmulcr = __FCmulcrPtr.asFunction<_Fcomplex Function(_Fcomplex, double)>(); + void updateDns( + ffi.Pointer s, + ) { + return _updateDns( + s, + ); + } + + late final _updateDnsPtr = + _lookup)>>( + 'updateDns'); + late final _updateDns = + _updateDnsPtr.asFunction)>(); + void start() { return _start(); } @@ -5264,20 +5278,6 @@ class ClashFFI { late final _getProxies = _getProxiesPtr.asFunction Function()>(); - void updateDns( - ffi.Pointer s, - ) { - return _updateDns( - s, - ); - } - - late final _updateDnsPtr = - _lookup)>>( - 'updateDns'); - late final _updateDns = - _updateDnsPtr.asFunction)>(); - void changeProxy( ffi.Pointer s, ) { @@ -5557,14 +5557,25 @@ class ClashFFI { late final _setProcessMap = _setProcessMapPtr.asFunction)>(); - ffi.Pointer getState() { - return _getState(); + ffi.Pointer getCurrentProfileName() { + return _getCurrentProfileName(); } - late final _getStatePtr = - _lookup Function()>>('getState'); - late final _getState = - _getStatePtr.asFunction Function()>(); + late final _getCurrentProfileNamePtr = + _lookup Function()>>( + 'getCurrentProfileName'); + late final _getCurrentProfileName = + _getCurrentProfileNamePtr.asFunction Function()>(); + + ffi.Pointer getAndroidVpnOptions() { + return _getAndroidVpnOptions(); + } + + late final _getAndroidVpnOptionsPtr = + _lookup Function()>>( + 'getAndroidVpnOptions'); + late final _getAndroidVpnOptions = + _getAndroidVpnOptionsPtr.asFunction Function()>(); void setState( ffi.Pointer s, @@ -5581,20 +5592,19 @@ class ClashFFI { _setStatePtr.asFunction)>(); void startTUN( - ffi.Pointer s, + int fd, int port, ) { return _startTUN( - s, + fd, port, ); } - late final _startTUNPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer, ffi.LongLong)>>('startTUN'); - late final _startTUN = - _startTUNPtr.asFunction, int)>(); + late final _startTUNPtr = + _lookup>( + 'startTUN'); + late final _startTUN = _startTUNPtr.asFunction(); ffi.Pointer getRunTime() { return _getRunTime(); diff --git a/lib/common/constant.dart b/lib/common/constant.dart index fae7c8cf..cb9fa1d6 100644 --- a/lib/common/constant.dart +++ b/lib/common/constant.dart @@ -1,8 +1,9 @@ import 'dart:io'; import 'dart:ui'; +import 'package:collection/collection.dart'; import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/models/clash_config.dart'; +import 'package:fl_clash/models/models.dart'; import 'package:flutter/material.dart'; import 'system.dart'; @@ -51,6 +52,21 @@ final filter = ImageFilter.blur( tileMode: TileMode.mirror, ); +const navigationItemListEquality = ListEquality(); +const connectionListEquality = ListEquality(); +const stringListEquality = ListEquality(); +const logListEquality = ListEquality(); +const groupListEquality = ListEquality(); +const externalProviderListEquality = ListEquality(); +const packageListEquality = ListEquality(); +const hotKeyActionListEquality = ListEquality(); +const stringAndStringMapEquality = MapEquality(); +const stringAndStringMapEntryIterableEquality = + IterableEquality>(); +const stringAndIntQMapEquality = MapEquality(); +const stringSetEquality = SetEquality(); +const keyboardModifierListEquality = SetEquality(); + const viewModeColumnsMap = { ViewMode.mobile: [2, 1], ViewMode.laptop: [3, 2], diff --git a/lib/common/launch.dart b/lib/common/launch.dart index 05133ec5..5a752e60 100644 --- a/lib/common/launch.dart +++ b/lib/common/launch.dart @@ -58,22 +58,7 @@ class AutoLaunch { Future windowsEnable() async { await disable(); - return windows?.runas( - 'schtasks', - [ - '/Create', - '/SC', - 'ONLOGON', - '/TN', - appName, - '/TR', - '"${Platform.resolvedExecutable}"', - '/RL', - 'HIGHEST', - '/F' - ].join(" "), - ) ?? - false; + return await windows?.registerTask(appName) ?? false; } Future disable() async { @@ -81,9 +66,9 @@ class AutoLaunch { } updateStatus(AutoLaunchState state) async { - final isOpenTun = state.isOpenTun; + final isAdminAutoLaunch = state.isAdminAutoLaunch; final isAutoLaunch = state.isAutoLaunch; - if (Platform.isWindows && isOpenTun) { + if (Platform.isWindows && isAdminAutoLaunch) { if (await windowsIsEnable == isAutoLaunch) return; if (isAutoLaunch) { final isEnable = await windowsEnable(); diff --git a/lib/common/measure.dart b/lib/common/measure.dart index dce0a59f..040d66c3 100644 --- a/lib/common/measure.dart +++ b/lib/common/measure.dart @@ -21,6 +21,7 @@ class Measure { } double? _bodyMediumHeight; + Size? _bodyLargeSize; double? _bodySmallHeight; double? _labelSmallHeight; double? _labelMediumHeight; @@ -30,17 +31,28 @@ class Measure { double get bodyMediumHeight { _bodyMediumHeight ??= computeTextSize( Text( - "", + "X", style: context.textTheme.bodyMedium, ), ).height; return _bodyMediumHeight!; } + + Size get bodyLargeSize { + _bodyLargeSize ??= computeTextSize( + Text( + "X", + style: context.textTheme.bodyLarge, + ), + ); + return _bodyLargeSize!; + } + double get bodySmallHeight { _bodySmallHeight ??= computeTextSize( Text( - "", + "X", style: context.textTheme.bodySmall, ), ).height; @@ -50,7 +62,7 @@ class Measure { double get labelSmallHeight { _labelSmallHeight ??= computeTextSize( Text( - "", + "X", style: context.textTheme.labelSmall, ), ).height; @@ -60,7 +72,7 @@ class Measure { double get labelMediumHeight { _labelMediumHeight ??= computeTextSize( Text( - "", + "X", style: context.textTheme.labelMedium, ), ).height; @@ -70,7 +82,7 @@ class Measure { double get titleLargeHeight { _titleLargeHeight ??= computeTextSize( Text( - "", + "X", style: context.textTheme.titleLarge, ), ).height; @@ -80,7 +92,7 @@ class Measure { double get titleMediumHeight { _titleMediumHeight ??= computeTextSize( Text( - "", + "X", style: context.textTheme.titleMedium, ), ).height; diff --git a/lib/common/path.dart b/lib/common/path.dart index 4945d608..7b60ad12 100644 --- a/lib/common/path.dart +++ b/lib/common/path.dart @@ -8,11 +8,12 @@ import 'constant.dart'; class AppPath { static AppPath? _instance; - Completer cacheDir = Completer(); + Completer dataDir = Completer(); Completer downloadDir = Completer(); + Completer tempDir = Completer(); + late String appDirPath; // Future _createDesktopCacheDir() async { - // final path = join(dirname(Platform.resolvedExecutable), 'cache'); // final dir = Directory(path); // if (await dir.exists()) { // await dir.create(recursive: true); @@ -21,8 +22,12 @@ class AppPath { // } AppPath._internal() { + appDirPath = join(dirname(Platform.resolvedExecutable)); getApplicationSupportDirectory().then((value) { - cacheDir.complete(value); + dataDir.complete(value); + }); + getTemporaryDirectory().then((value){ + tempDir.complete(value); }); getDownloadsDirectory().then((value) { downloadDir.complete(value); @@ -49,12 +54,12 @@ class AppPath { } Future getHomeDirPath() async { - final directory = await cacheDir.future; + final directory = await dataDir.future; return directory.path; } Future getProfilesPath() async { - final directory = await cacheDir.future; + final directory = await dataDir.future; return join(directory.path, profilesDirectoryName); } @@ -63,6 +68,11 @@ class AppPath { final directory = await getProfilesPath(); return join(directory, "$id.yaml"); } + + Future get tempPath async { + final directory = await tempDir.future; + return directory.path; + } } final appPath = AppPath(); diff --git a/lib/common/preferences.dart b/lib/common/preferences.dart index c49b4b58..678cc044 100644 --- a/lib/common/preferences.dart +++ b/lib/common/preferences.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:flutter/cupertino.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../models/models.dart'; @@ -28,7 +29,8 @@ class Preferences { try { return ClashConfig.fromJson(clashConfigMap); } catch (e) { - throw e.toString(); + debugPrint(e.toString()); + return null; } } @@ -48,7 +50,8 @@ class Preferences { try { return Config.fromJson(configMap); } catch (e) { - throw e.toString(); + debugPrint(e.toString()); + return null; } } diff --git a/lib/common/request.dart b/lib/common/request.dart index 03840dc4..abbbbb39 100644 --- a/lib/common/request.dart +++ b/lib/common/request.dart @@ -1,4 +1,3 @@ -import 'dart:math'; import 'dart:typed_data'; import 'package:dio/dio.dart'; @@ -77,7 +76,7 @@ class Request { }; Future checkIp({CancelToken? cancelToken}) async { - for (final source in _ipInfoSources.entries.toList()..shuffle(Random())) { + for (final source in _ipInfoSources.entries) { try { final response = await _dio .get>( diff --git a/lib/common/scroll.dart b/lib/common/scroll.dart index 054a0487..cb48baff 100644 --- a/lib/common/scroll.dart +++ b/lib/common/scroll.dart @@ -25,3 +25,18 @@ class HiddenBarScrollBehavior extends BaseScrollBehavior { return child; } } + +class ShowBarScrollBehavior extends BaseScrollBehavior { + @override + Widget buildScrollbar( + BuildContext context, + Widget child, + ScrollableDetails details, + ) { + return Scrollbar( + interactive: true, + controller: details.controller, + child: child, + ); + } +} diff --git a/lib/common/string.dart b/lib/common/string.dart index b83c89ec..ec66eedd 100644 --- a/lib/common/string.dart +++ b/lib/common/string.dart @@ -1,3 +1,8 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; + extension StringExtension on String { bool get isUrl { return RegExp(r'^(http|https|ftp)://').hasMatch(this); @@ -8,4 +13,38 @@ extension StringExtension on String { other.toLowerCase(), ); } + + List get encodeUtf16LeWithBom { + final byteData = ByteData(length * 2); + final bom = [0xFF, 0xFE]; + for (int i = 0; i < length; i++) { + int charCode = codeUnitAt(i); + byteData.setUint16(i * 2, charCode, Endian.little); + } + return bom + byteData.buffer.asUint8List(); + } + + Uint8List? get getBase64 { + final regExp = RegExp(r'base64,(.*)'); + final match = regExp.firstMatch(this); + final realValue = match?.group(1) ?? ''; + if (realValue.isEmpty) { + return null; + } + try { + return base64.decode(realValue); + } catch (e) { + return null; + } + } + + bool get isRegex { + try { + RegExp(this); + return true; + } catch (e) { + debugPrint(e.toString()); + return false; + } + } } diff --git a/lib/common/system.dart b/lib/common/system.dart index 433f609c..c6c68b10 100644 --- a/lib/common/system.dart +++ b/lib/common/system.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:fl_clash/plugins/app.dart'; -import 'package:fl_clash/state.dart'; import 'package:flutter/services.dart'; import 'window.dart'; diff --git a/lib/common/windows.dart b/lib/common/windows.dart index 4b7ccd9a..548f2b1d 100644 --- a/lib/common/windows.dart +++ b/lib/common/windows.dart @@ -1,6 +1,8 @@ import 'dart:ffi'; import 'dart:io'; import 'package:ffi/ffi.dart'; +import 'package:fl_clash/common/common.dart'; +import 'package:path/path.dart'; class Windows { static Windows? _instance; @@ -54,6 +56,62 @@ class Windows { } return true; } + + Future registerTask(String appName) async { + final taskXml = ''' + + + + + InteractiveToken + HighestAvailable + + + + + + + Parallel + false + false + false + false + false + + false + false + + true + true + false + false + false + PT72H + 7 + + + + "${Platform.resolvedExecutable}" + + +'''; + final taskPath = join(await appPath.tempPath, "task.xml"); + await File(taskPath).create(recursive: true); + await File(taskPath) + .writeAsBytes(taskXml.encodeUtf16LeWithBom, flush: true); + final commandLine = [ + '/Create', + '/TN', + appName, + '/XML', + "%s", + '/F', + ].join(" "); + return runas( + 'schtasks', + commandLine.replaceFirst("%s", taskPath), + ); + } } final windows = Platform.isWindows ? Windows() : null; diff --git a/lib/controller.dart b/lib/controller.dart index c426710d..0747ca99 100644 --- a/lib/controller.dart +++ b/lib/controller.dart @@ -7,7 +7,6 @@ import 'dart:typed_data'; import 'package:archive/archive.dart'; import 'package:fl_clash/common/archive.dart'; import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/plugins/app.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; import 'package:path/path.dart'; @@ -228,7 +227,7 @@ class AppController { } handleBackOrExit() async { - if (config.isMinimizeOnExit) { + if (config.appSetting.minimizeOnExit) { if (system.isDesktop) { await savePreferences(); } @@ -247,7 +246,7 @@ class AppController { } updateLogStatus() { - if (config.openLogs) { + if (config.appSetting.openLogs) { clashCore.startLog(); } else { clashCore.stopLog(); @@ -256,7 +255,7 @@ class AppController { } autoCheckUpdate() async { - if (!config.autoCheckUpdate) return; + if (!config.appSetting.autoCheckUpdate) return; final res = await request.checkForUpdate(); checkUpdateResultHandle(data: res); } @@ -310,7 +309,7 @@ class AppController { handleExit(); } updateLogStatus(); - if (!config.silentLaunch) { + if (!config.appSetting.silentLaunch) { window?.show(); } if (Platform.isAndroid) { @@ -319,7 +318,7 @@ class AppController { if (globalState.isStart) { await updateStatus(true); } else { - await updateStatus(config.autoRun); + await updateStatus(config.appSetting.autoRun); } autoUpdateProfiles(); autoCheckUpdate(); @@ -334,7 +333,7 @@ class AppController { return; } appState.currentLabel = appState.currentNavigationItems[index].label; - if ((config.isAnimateToPage || hasAnimate)) { + if ((config.appSetting.isAnimateToPage || hasAnimate)) { globalState.pageController?.animateToPage( index, duration: kTabScrollDuration, @@ -410,7 +409,9 @@ class AppController { ), TextButton( onPressed: () { - config.isDisclaimerAccepted = true; + config.appSetting = config.appSetting.copyWith( + disclaimerAccepted: true, + ); Navigator.of(context).pop(true); }, child: Text(appLocalizations.agree), @@ -422,7 +423,7 @@ class AppController { } Future handlerDisclaimer() async { - if (config.isDisclaimerAccepted) { + if (config.appSetting.disclaimerAccepted) { return true; } return showDisclaimer(); @@ -514,7 +515,7 @@ class AppController { } List getSortProxies(List proxies) { - return switch (config.proxiesSortType) { + return switch (config.proxiesStyle.sortType) { ProxiesSortType.none => proxies, ProxiesSortType.delay => _sortOfDelay(proxies), ProxiesSortType.name => _sortOfName(proxies), @@ -545,7 +546,15 @@ class AppController { } updateAutoLaunch() { - config.autoLaunch = !config.autoLaunch; + config.appSetting = config.appSetting.copyWith( + autoLaunch: !config.appSetting.autoLaunch, + ); + } + + updateAdminAutoLaunch() { + config.appSetting = config.appSetting.copyWith( + adminAutoLaunch: !config.appSetting.adminAutoLaunch, + ); } updateVisible() async { diff --git a/lib/enum/enum.dart b/lib/enum/enum.dart index e095b590..847b8a24 100644 --- a/lib/enum/enum.dart +++ b/lib/enum/enum.dart @@ -15,7 +15,7 @@ extension GroupTypeExtension on GroupType { ) .toList(); - bool get isURLTestOrFallback { + bool get isURLTestOrFallback { return [GroupType.URLTest, GroupType.Fallback].contains(this); } @@ -156,3 +156,9 @@ enum HotAction { proxy, tun, } + +enum ProxiesIconStyle { + standard, + none, + icon, +} diff --git a/lib/fragments/application_setting.dart b/lib/fragments/application_setting.dart index 07e1429f..14de55d5 100644 --- a/lib/fragments/application_setting.dart +++ b/lib/fragments/application_setting.dart @@ -8,6 +8,84 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +class CloseConnectionsSwitch extends StatelessWidget { + const CloseConnectionsSwitch({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, config) => config.appSetting.closeConnections, + builder: (_, closeConnections, __) { + return ListItem.switchItem( + title: Text(appLocalizations.autoCloseConnections), + subtitle: Text(appLocalizations.autoCloseConnectionsDesc), + delegate: SwitchDelegate( + value: closeConnections, + onChanged: (value) async { + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + closeConnections: value, + ); + }, + ), + ); + }, + ); + } +} + +class UsageSwitch extends StatelessWidget { + const UsageSwitch({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, config) => config.appSetting.onlyProxy, + builder: (_, onlyProxy, __) { + return ListItem.switchItem( + title: Text(appLocalizations.onlyStatisticsProxy), + subtitle: Text(appLocalizations.onlyStatisticsProxyDesc), + delegate: SwitchDelegate( + value: onlyProxy, + onChanged: (bool value) async { + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + onlyProxy: value, + ); + }, + ), + ); + }, + ); + } +} + +class AdminAutoLaunchItem extends StatelessWidget { + const AdminAutoLaunchItem({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, config) => config.appSetting.adminAutoLaunch, + builder: (_, adminAutoLaunch, __) { + return ListItem.switchItem( + title: Text(appLocalizations.adminAutoLaunch), + subtitle: Text(appLocalizations.adminAutoLaunchDesc), + delegate: SwitchDelegate( + value: adminAutoLaunch, + onChanged: (bool value) async { + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + adminAutoLaunch: value, + ); + }, + ), + ); + }, + ); + } +} + class ApplicationSettingFragment extends StatelessWidget { const ApplicationSettingFragment({super.key}); @@ -20,17 +98,18 @@ class ApplicationSettingFragment extends StatelessWidget { Widget build(BuildContext context) { List items = [ Selector( - selector: (_, config) => config.isMinimizeOnExit, + selector: (_, config) => config.appSetting.minimizeOnExit, builder: (_, isMinimizeOnExit, child) { return ListItem.switchItem( - leading: const Icon(Icons.back_hand), title: Text(appLocalizations.minimizeOnExit), subtitle: Text(appLocalizations.minimizeOnExitDesc), delegate: SwitchDelegate( value: isMinimizeOnExit, onChanged: (bool value) { final config = context.read(); - config.isMinimizeOnExit = value; + config.appSetting = config.appSetting.copyWith( + minimizeOnExit: value, + ); }, ), ); @@ -38,52 +117,57 @@ class ApplicationSettingFragment extends StatelessWidget { ), if (system.isDesktop) Selector( - selector: (_, config) => config.autoLaunch, + selector: (_, config) => config.appSetting.autoLaunch, builder: (_, autoLaunch, child) { return ListItem.switchItem( - leading: const Icon(Icons.rocket_launch), title: Text(appLocalizations.autoLaunch), subtitle: Text(appLocalizations.autoLaunchDesc), delegate: SwitchDelegate( value: autoLaunch, onChanged: (bool value) { - final config = context.read(); - config.autoLaunch = value; + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + autoLaunch: value, + ); }, ), ); }, ), + if(Platform.isWindows) + const AdminAutoLaunchItem(), if (system.isDesktop) Selector( - selector: (_, config) => config.silentLaunch, + selector: (_, config) => config.appSetting.silentLaunch, builder: (_, silentLaunch, child) { return ListItem.switchItem( - leading: const Icon(Icons.expand_circle_down), title: Text(appLocalizations.silentLaunch), subtitle: Text(appLocalizations.silentLaunchDesc), delegate: SwitchDelegate( value: silentLaunch, onChanged: (bool value) { - final config = context.read(); - config.silentLaunch = value; + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + silentLaunch: value, + ); }, ), ); }, ), Selector( - selector: (_, config) => config.autoRun, + selector: (_, config) => config.appSetting.autoRun, builder: (_, autoRun, child) { return ListItem.switchItem( - leading: const Icon(Icons.not_started), title: Text(appLocalizations.autoRun), subtitle: Text(appLocalizations.autoRunDesc), delegate: SwitchDelegate( value: autoRun, onChanged: (bool value) { - final config = context.read(); - config.autoRun = value; + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + autoRun: value, + ); }, ), ); @@ -91,17 +175,18 @@ class ApplicationSettingFragment extends StatelessWidget { ), if (Platform.isAndroid) Selector( - selector: (_, config) => config.isExclude, + selector: (_, config) => config.appSetting.hidden, builder: (_, isExclude, child) { return ListItem.switchItem( - leading: const Icon(Icons.visibility_off), title: Text(appLocalizations.exclude), subtitle: Text(appLocalizations.excludeDesc), delegate: SwitchDelegate( value: isExclude, onChanged: (value) { - final config = context.read(); - config.isExclude = value; + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + hidden: value, + ); }, ), ); @@ -109,52 +194,56 @@ class ApplicationSettingFragment extends StatelessWidget { ), if (Platform.isAndroid) Selector( - selector: (_, config) => config.isAnimateToPage, + selector: (_, config) => config.appSetting.isAnimateToPage, builder: (_, isAnimateToPage, child) { return ListItem.switchItem( - leading: const Icon(Icons.animation), title: Text(appLocalizations.tabAnimation), subtitle: Text(appLocalizations.tabAnimationDesc), delegate: SwitchDelegate( value: isAnimateToPage, onChanged: (value) { - final config = context.read(); - config.isAnimateToPage = value; + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + isAnimateToPage: value, + ); }, ), ); }, ), Selector( - selector: (_, config) => config.openLogs, + selector: (_, config) => config.appSetting.openLogs, builder: (_, openLogs, child) { return ListItem.switchItem( - leading: const Icon(Icons.bug_report), title: Text(appLocalizations.logcat), subtitle: Text(appLocalizations.logcatDesc), delegate: SwitchDelegate( value: openLogs, onChanged: (bool value) { - final config = context.read(); - config.openLogs = value; - globalState.appController.updateLogStatus(); + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + openLogs: value, + ); }, ), ); }, ), + const CloseConnectionsSwitch(), + const UsageSwitch(), Selector( - selector: (_, config) => config.autoCheckUpdate, + selector: (_, config) => config.appSetting.autoCheckUpdate, builder: (_, autoCheckUpdate, child) { return ListItem.switchItem( - leading: const Icon(Icons.system_update), title: Text(appLocalizations.autoCheckUpdate), subtitle: Text(appLocalizations.autoCheckUpdateDesc), delegate: SwitchDelegate( value: autoCheckUpdate, onChanged: (bool value) { - final config = context.read(); - config.autoCheckUpdate = value; + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + autoCheckUpdate: value, + ); }, ), ); diff --git a/lib/fragments/config/app.dart b/lib/fragments/config/app.dart deleted file mode 100644 index 2a12fae0..00000000 --- a/lib/fragments/config/app.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/models/config.dart'; -import 'package:fl_clash/state.dart'; -import 'package:fl_clash/widgets/widgets.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class CloseConnectionsSwitch extends StatelessWidget { - const CloseConnectionsSwitch({super.key}); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, config) => config.isCloseConnections, - builder: (_, isCloseConnections, __) { - return ListItem.switchItem( - leading: const Icon(Icons.auto_delete_outlined), - title: Text(appLocalizations.autoCloseConnections), - subtitle: Text(appLocalizations.autoCloseConnectionsDesc), - delegate: SwitchDelegate( - value: isCloseConnections, - onChanged: (bool value) async { - final appController = globalState.appController; - appController.config.isCloseConnections = value; - }, - ), - ); - }, - ); - } -} - -class UsageSwitch extends StatelessWidget { - const UsageSwitch({super.key}); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, config) => config.onlyProxy, - builder: (_, onlyProxy, __) { - return ListItem.switchItem( - leading: const Icon(Icons.data_usage_outlined), - title: Text(appLocalizations.onlyStatisticsProxy), - subtitle: Text(appLocalizations.onlyStatisticsProxyDesc), - delegate: SwitchDelegate( - value: onlyProxy, - onChanged: (bool value) async { - final appController = globalState.appController; - appController.config.onlyProxy = value; - }, - ), - ); - }, - ); - } -} - -final appItems = [ - const CloseConnectionsSwitch(), - const UsageSwitch(), -]; diff --git a/lib/fragments/config/config.dart b/lib/fragments/config/config.dart index 5647e332..f06a9499 100644 --- a/lib/fragments/config/config.dart +++ b/lib/fragments/config/config.dart @@ -1,10 +1,7 @@ -import 'dart:io'; - import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/fragments/config/app.dart'; import 'package:fl_clash/fragments/config/dns.dart'; import 'package:fl_clash/fragments/config/general.dart'; -import 'package:fl_clash/fragments/config/vpn.dart'; +import 'package:fl_clash/fragments/config/network.dart'; import 'package:fl_clash/widgets/widgets.dart'; import 'package:flutter/material.dart'; @@ -20,36 +17,16 @@ class _ConfigFragmentState extends State { Widget build(BuildContext context) { List items = [ ListItem.open( - title: Text(appLocalizations.app), - subtitle: Text(appLocalizations.appDesc), - leading: const Icon(Icons.settings_applications), + title: Text(appLocalizations.network), + subtitle: Text(appLocalizations.networkDesc), + leading: const Icon(Icons.vpn_key), delegate: OpenDelegate( - title: appLocalizations.app, + title: appLocalizations.network, + isScaffold: true, isBlur: false, - widget: generateListView( - appItems - .separated( - const Divider( - height: 0, - ), - ) - .toList(), - ), + widget: const NetworkListView(), ), ), - if (Platform.isAndroid) - ListItem.open( - title: const Text("VPN"), - subtitle: Text(appLocalizations.vpnDesc), - leading: const Icon(Icons.vpn_key), - delegate: OpenDelegate( - title: "VPN", - isBlur: false, - widget: generateListView( - vpnItems, - ), - ), - ), ListItem.open( title: Text(appLocalizations.general), subtitle: Text(appLocalizations.generalDesc), diff --git a/lib/fragments/config/dns.dart b/lib/fragments/config/dns.dart index f7425f69..c2cc5086 100644 --- a/lib/fragments/config/dns.dart +++ b/lib/fragments/config/dns.dart @@ -219,26 +219,17 @@ class FakeIpFilterItem extends StatelessWidget { title: appLocalizations.fakeipFilter, widget: Selector>( selector: (_, clashConfig) => clashConfig.dns.fakeIpFilter, - shouldRebuild: (prev, next) => - !const ListEquality().equals(prev, next), + shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next), builder: (_, fakeIpFilter, __) { - return UpdatePage( + return ListPage( title: appLocalizations.fakeipFilter, items: fakeIpFilter, titleBuilder: (item) => Text(item), - onRemove: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - clashConfig.dns = dns.copyWith( - fakeIpFilter: List.from(dns.fakeIpFilter)..remove(value), - ); - }, - onAdd: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; - if (fakeIpFilter.contains(value)) return; clashConfig.dns = dns.copyWith( - fakeIpFilter: List.from(dns.fakeIpFilter)..add(value), + fakeIpFilter: List.from(items), ); }, ); @@ -263,28 +254,17 @@ class DefaultNameserverItem extends StatelessWidget { title: appLocalizations.defaultNameserver, widget: Selector>( selector: (_, clashConfig) => clashConfig.dns.defaultNameserver, - shouldRebuild: (prev, next) => - !const ListEquality().equals(prev, next), + shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next), builder: (_, defaultNameserver, __) { - return UpdatePage( + return ListPage( title: appLocalizations.defaultNameserver, items: defaultNameserver, titleBuilder: (item) => Text(item), - onRemove: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; clashConfig.dns = dns.copyWith( - defaultNameserver: List.from(dns.defaultNameserver) - ..remove(value), - ); - }, - onAdd: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - if (defaultNameserver.contains(value)) return; - clashConfig.dns = dns.copyWith( - defaultNameserver: List.from(dns.defaultNameserver) - ..add(value), + defaultNameserver: List.from(items), ); }, ); @@ -309,26 +289,17 @@ class NameserverItem extends StatelessWidget { isBlur: false, widget: Selector>( selector: (_, clashConfig) => clashConfig.dns.nameserver, - shouldRebuild: (prev, next) => - !const ListEquality().equals(prev, next), + shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next), builder: (_, nameserver, __) { - return UpdatePage( + return ListPage( title: "域名服务器", items: nameserver, titleBuilder: (item) => Text(item), - onRemove: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - clashConfig.dns = dns.copyWith( - nameserver: List.from(dns.nameserver)..remove(value), - ); - }, - onAdd: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; - if (nameserver.contains(value)) return; clashConfig.dns = dns.copyWith( - nameserver: List.from(dns.nameserver)..add(value), + nameserver: List.from(items), ); }, ); @@ -408,25 +379,16 @@ class NameserverPolicyItem extends StatelessWidget { shouldRebuild: (prev, next) => !const MapEquality().equals(prev, next), builder: (_, nameserverPolicy, __) { - return UpdatePage( + return ListPage( title: appLocalizations.nameserverPolicy, items: nameserverPolicy.entries, titleBuilder: (item) => Text(item.key), subtitleBuilder: (item) => Text(item.value), - onRemove: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - clashConfig.dns = dns.copyWith( - nameserverPolicy: Map.from(dns.nameserverPolicy) - ..remove(value.key), - ); - }, - onAdd: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; clashConfig.dns = dns.copyWith( - nameserverPolicy: Map.from(dns.nameserverPolicy) - ..addEntries([value]), + nameserverPolicy: Map.fromEntries(items), ); }, ); @@ -451,28 +413,17 @@ class ProxyServerNameserverItem extends StatelessWidget { title: appLocalizations.proxyNameserver, widget: Selector>( selector: (_, clashConfig) => clashConfig.dns.proxyServerNameserver, - shouldRebuild: (prev, next) => - !const ListEquality().equals(prev, next), + shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next), builder: (_, proxyServerNameserver, __) { - return UpdatePage( + return ListPage( title: appLocalizations.proxyNameserver, items: proxyServerNameserver, titleBuilder: (item) => Text(item), - onRemove: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; clashConfig.dns = dns.copyWith( - proxyServerNameserver: List.from(dns.proxyServerNameserver) - ..remove(value), - ); - }, - onAdd: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - if (proxyServerNameserver.contains(value)) return; - clashConfig.dns = dns.copyWith( - proxyServerNameserver: List.from(dns.proxyServerNameserver) - ..add(value), + proxyServerNameserver: List.from(items), ); }, ); @@ -497,26 +448,17 @@ class FallbackItem extends StatelessWidget { title: appLocalizations.fallback, widget: Selector>( selector: (_, clashConfig) => clashConfig.dns.fallback, - shouldRebuild: (prev, next) => - !const ListEquality().equals(prev, next), + shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next), builder: (_, fallback, __) { - return UpdatePage( + return ListPage( title: appLocalizations.fallback, items: fallback, titleBuilder: (item) => Text(item), - onRemove: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; clashConfig.dns = dns.copyWith( - fallback: List.from(dns.fallback)..remove(value), - ); - }, - onAdd: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - if (fallback.contains(value)) return; - clashConfig.dns = dns.copyWith( - fallback: List.from(dns.fallback)..add(value), + fallback: List.from(items), ); }, ); @@ -607,28 +549,18 @@ class GeositeItem extends StatelessWidget { title: "Geosite", widget: Selector>( selector: (_, clashConfig) => clashConfig.dns.fallbackFilter.geosite, - shouldRebuild: (prev, next) => - !const ListEquality().equals(prev, next), + shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next), builder: (_, geosite, __) { - return UpdatePage( + return ListPage( title: "Geosite", items: geosite, titleBuilder: (item) => Text(item), - onRemove: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; clashConfig.dns = dns.copyWith( fallbackFilter: dns.fallbackFilter.copyWith( - geosite: List.from(geosite)..remove(value), - ), - ); - }, - onAdd: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - clashConfig.dns = dns.copyWith( - fallbackFilter: dns.fallbackFilter.copyWith( - geosite: List.from(geosite)..add(value), + geosite: List.from(items), ), ); }, @@ -653,28 +585,18 @@ class IpcidrItem extends StatelessWidget { title: appLocalizations.ipcidr, widget: Selector>( selector: (_, clashConfig) => clashConfig.dns.fallbackFilter.ipcidr, - shouldRebuild: (prev, next) => - !const ListEquality().equals(prev, next), + shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next), builder: (_, ipcidr, __) { - return UpdatePage( + return ListPage( title: appLocalizations.ipcidr, items: ipcidr, titleBuilder: (item) => Text(item), - onRemove: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; clashConfig.dns = dns.copyWith( fallbackFilter: dns.fallbackFilter.copyWith( - ipcidr: List.from(ipcidr)..remove(value), - ), - ); - }, - onAdd: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - clashConfig.dns = dns.copyWith( - fallbackFilter: dns.fallbackFilter.copyWith( - ipcidr: List.from(ipcidr)..add(value), + ipcidr: List.from(items), ), ); }, @@ -699,28 +621,18 @@ class DomainItem extends StatelessWidget { title: appLocalizations.domain, widget: Selector>( selector: (_, clashConfig) => clashConfig.dns.fallbackFilter.domain, - shouldRebuild: (prev, next) => - !const ListEquality().equals(prev, next), + shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next), builder: (_, domain, __) { - return UpdatePage( + return ListPage( title: appLocalizations.domain, items: domain, titleBuilder: (item) => Text(item), - onRemove: (value) { - final clashConfig = globalState.appController.clashConfig; - final dns = clashConfig.dns; - clashConfig.dns = dns.copyWith( - fallbackFilter: dns.fallbackFilter.copyWith( - domain: List.from(domain)..remove(value), - ), - ); - }, - onAdd: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; final dns = clashConfig.dns; clashConfig.dns = dns.copyWith( fallbackFilter: dns.fallbackFilter.copyWith( - domain: List.from(domain)..add(value), + domain: List.from(items), ), ); }, @@ -799,16 +711,16 @@ class DnsListView extends StatelessWidget { IconButton( onPressed: () { globalState.showMessage( - title: appLocalizations.resetDns, + title: appLocalizations.reset, message: TextSpan( - text: appLocalizations.dnsResetTip, + text: appLocalizations.resetTip, ), onTab: () { - globalState.appController.clashConfig.dns = const Dns(); + globalState.appController.clashConfig.dns = defaultDns; Navigator.of(context).pop(); }); }, - tooltip: appLocalizations.resetDns, + tooltip: appLocalizations.reset, icon: const Icon( Icons.replay, ), diff --git a/lib/fragments/config/general.dart b/lib/fragments/config/general.dart index 9725217f..2f0b224b 100644 --- a/lib/fragments/config/general.dart +++ b/lib/fragments/config/general.dart @@ -119,7 +119,7 @@ class TestUrlItem extends StatelessWidget { @override Widget build(BuildContext context) { return Selector( - selector: (_, config) => config.testUrl, + selector: (_, config) => config.appSetting.testUrl, builder: (_, value, __) { return ListItem.input( leading: const Icon(Icons.timeline), @@ -135,7 +135,10 @@ class TestUrlItem extends StatelessWidget { if (!value.isUrl) { throw "Invalid url"; } - globalState.appController.config.testUrl = value; + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + testUrl: value, + ); } catch (e) { globalState.showMessage( title: appLocalizations.testUrl, @@ -212,19 +215,14 @@ class HostsItem extends StatelessWidget { !const MapEquality().equals(prev, next), builder: (_, hosts, ___) { final entries = hosts.entries; - return UpdatePage( + return ListPage( title: "Hosts", items: entries, titleBuilder: (item) => Text(item.key), subtitleBuilder: (item) => Text(item.value), - onRemove: (value) { - final clashConfig = globalState.appController.clashConfig; - clashConfig.hosts = Map.from(hosts)..remove(value.key); - }, - onAdd: (value) { + onChange: (items){ final clashConfig = globalState.appController.clashConfig; - clashConfig.hosts = Map.from(clashConfig.hosts) - ..addEntries([value]); + clashConfig.hosts = Map.fromEntries(items); }, ); }, diff --git a/lib/fragments/config/network.dart b/lib/fragments/config/network.dart new file mode 100644 index 00000000..70948d56 --- /dev/null +++ b/lib/fragments/config/network.dart @@ -0,0 +1,270 @@ +import 'dart:io'; + +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; +import 'package:fl_clash/state.dart'; +import 'package:fl_clash/widgets/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class VPNSwitch extends StatelessWidget { + const VPNSwitch({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, config) => config.vpnProps.enable, + builder: (_, enable, __) { + return ListItem.switchItem( + title: const Text("VPN"), + subtitle: Text(appLocalizations.vpnEnableDesc), + delegate: SwitchDelegate( + value: enable, + onChanged: (value) async { + final config = globalState.appController.config; + config.vpnProps = config.vpnProps.copyWith( + enable: value, + ); + }, + ), + ); + }, + ); + } +} + +class TUNItem extends StatelessWidget { + const TUNItem({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, config) => config.vpnProps.enable, + builder: (_, enable, __) { + return ListItem.switchItem( + title: Text(appLocalizations.tun), + subtitle: Text(appLocalizations.tunDesc), + delegate: SwitchDelegate( + value: enable, + onChanged: (value) async { + final clashConfig = globalState.appController.clashConfig; + clashConfig.tun = clashConfig.tun.copyWith( + enable: value, + ); + }, + ), + ); + }, + ); + } +} + +class AllowBypassSwitch extends StatelessWidget { + const AllowBypassSwitch({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, config) => config.vpnProps.allowBypass, + builder: (_, allowBypass, __) { + return ListItem.switchItem( + title: Text(appLocalizations.allowBypass), + subtitle: Text(appLocalizations.allowBypassDesc), + delegate: SwitchDelegate( + value: allowBypass, + onChanged: (bool value) async { + final config = globalState.appController.config; + final vpnProps = config.vpnProps; + config.vpnProps = vpnProps.copyWith( + allowBypass: value, + ); + }, + ), + ); + }, + ); + } +} + +class SystemProxySwitch extends StatelessWidget { + const SystemProxySwitch({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, config) => config.vpnProps.systemProxy, + builder: (_, systemProxy, __) { + return ListItem.switchItem( + title: Text(appLocalizations.systemProxy), + subtitle: Text(appLocalizations.systemProxyDesc), + delegate: SwitchDelegate( + value: systemProxy, + onChanged: (bool value) async { + final config = globalState.appController.config; + final vpnProps = config.vpnProps; + config.vpnProps = vpnProps.copyWith( + systemProxy: value, + ); + }, + ), + ); + }, + ); + } +} + +class Ipv6Switch extends StatelessWidget { + const Ipv6Switch({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, config) => config.vpnProps.ipv6, + builder: (_, ipv6, __) { + return ListItem.switchItem( + title: const Text("IPv6"), + subtitle: Text(appLocalizations.ipv6InboundDesc), + delegate: SwitchDelegate( + value: ipv6, + onChanged: (bool value) async { + final config = globalState.appController.config; + final vpnProps = config.vpnProps; + config.vpnProps = vpnProps.copyWith( + ipv6: value, + ); + }, + ), + ); + }, + ); + } +} + +class TunStackItem extends StatelessWidget { + const TunStackItem({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, clashConfig) => clashConfig.tun.stack, + builder: (_, stack, __) { + return ListItem.options( + title: Text(appLocalizations.stackMode), + subtitle: Text(stack.name), + delegate: OptionsDelegate( + value: stack, + options: TunStack.values, + textBuilder: (value) => value.name, + onChanged: (value) { + if (value == null) { + return; + } + final clashConfig = globalState.appController.clashConfig; + clashConfig.tun = clashConfig.tun.copyWith( + stack: value, + ); + }, + title: appLocalizations.stackMode, + ), + ); + }, + ); + } +} + +class BypassDomainItem extends StatelessWidget { + const BypassDomainItem({super.key}); + + @override + Widget build(BuildContext context) { + return ListItem.open( + title: Text(appLocalizations.bypassDomain), + subtitle: Text(appLocalizations.bypassDomainDesc), + delegate: OpenDelegate( + isBlur: false, + title: appLocalizations.bypassDomain, + widget: Selector>( + selector: (_, config) => config.vpnProps.bypassDomain, + shouldRebuild: (prev, next) => + !stringListEquality.equals(prev, next), + builder: (_, bypassDomain, __) { + return ListPage( + title: appLocalizations.bypassDomain, + items: bypassDomain, + titleBuilder: (item) => Text(item), + onChange: (items){ + final config = globalState.appController.config; + config.vpnProps = config.vpnProps.copyWith( + bypassDomain: List.from(items), + ); + }, + ); + }, + ), + extendPageWidth: 360, + ), + ); + } +} + +final networkItems = [ + Platform.isAndroid ? const VPNSwitch() : const TUNItem(), + if (Platform.isAndroid) + ...generateSection( + title: "VPN", + items: [ + const SystemProxySwitch(), + const AllowBypassSwitch(), + const Ipv6Switch(), + const BypassDomainItem(), + ], + ), + ...generateSection( + title: appLocalizations.options, + items: [ + const TunStackItem(), + ], + ), +]; + +class NetworkListView extends StatelessWidget { + const NetworkListView({super.key}); + + _initActions(BuildContext context) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + final commonScaffoldState = + context.findAncestorStateOfType(); + commonScaffoldState?.actions = [ + IconButton( + onPressed: () { + globalState.showMessage( + title: appLocalizations.reset, + message: TextSpan( + text: appLocalizations.resetTip, + ), + onTab: () { + final appController = globalState.appController; + appController.config.vpnProps = defaultVpnProps; + appController.clashConfig.tun = defaultTun; + Navigator.of(context).pop(); + }, + ); + }, + tooltip: appLocalizations.reset, + icon: const Icon( + Icons.replay, + ), + ) + ]; + }); + } + + @override + Widget build(BuildContext context) { + _initActions(context); + return generateListView( + networkItems, + ); + } +} diff --git a/lib/fragments/config/vpn.dart b/lib/fragments/config/vpn.dart deleted file mode 100644 index 3a9dafe8..00000000 --- a/lib/fragments/config/vpn.dart +++ /dev/null @@ -1,169 +0,0 @@ -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/models/models.dart'; -import 'package:fl_clash/state.dart'; -import 'package:fl_clash/widgets/widgets.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class VPNSwitch extends StatelessWidget { - const VPNSwitch({super.key}); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, config) => config.vpnProps.enable, - builder: (_, enable, __) { - return ListItem.switchItem( - leading: const Icon(Icons.stacked_line_chart), - title: const Text("VPN"), - subtitle: Text(appLocalizations.vpnEnableDesc), - delegate: SwitchDelegate( - value: enable, - onChanged: (bool value) async { - final config = globalState.appController.config; - final vpnProps = config.vpnProps; - config.vpnProps = vpnProps.copyWith( - enable: value, - ); - }, - ), - ); - }, - ); - } -} - -class VPNDisabledContainer extends StatelessWidget { - final Widget child; - - const VPNDisabledContainer( - this.child, { - super.key, - }); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, config) => config.vpnProps.enable, - builder: (_, enable, child) { - return AbsorbPointer( - absorbing: !enable, - child: DisabledMask( - status: !enable, - child: child!, - ), - ); - }, - child: child, - ); - } -} - -class AllowBypassSwitch extends StatelessWidget { - const AllowBypassSwitch({super.key}); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, config) => config.vpnProps.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 config = globalState.appController.config; - final vpnProps = config.vpnProps; - config.vpnProps = vpnProps.copyWith( - allowBypass: value, - ); - }, - ), - ); - }, - ); - } -} - -class SystemProxySwitch extends StatelessWidget { - const SystemProxySwitch({super.key}); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, config) => config.vpnProps.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 config = globalState.appController.config; - final vpnProps = config.vpnProps; - config.vpnProps = vpnProps.copyWith( - systemProxy: value, - ); - }, - ), - ); - }, - ); - } -} - -class Ipv6Switch extends StatelessWidget { - const Ipv6Switch({super.key}); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, config) => config.vpnProps.ipv6, - builder: (_, ipv6, __) { - return ListItem.switchItem( - leading: const Icon(Icons.water_outlined), - title: const Text("IPv6"), - subtitle: Text(appLocalizations.ipv6InboundDesc), - delegate: SwitchDelegate( - value: ipv6, - onChanged: (bool value) async { - final config = globalState.appController.config; - final vpnProps = config.vpnProps; - config.vpnProps = vpnProps.copyWith( - ipv6: value, - ); - }, - ), - ); - }, - ); - } -} - -class VpnOptions extends StatelessWidget { - const VpnOptions({super.key}); - - @override - Widget build(BuildContext context) { - return VPNDisabledContainer( - Column( - children: generateSection( - title: appLocalizations.options, - items: [ - const SystemProxySwitch(), - const AllowBypassSwitch(), - const Ipv6Switch(), - ], - ), - ), - ); - } -} - -final vpnItems = [ - const VPNSwitch(), - const VpnOptions(), -]; diff --git a/lib/fragments/hotkey.dart b/lib/fragments/hotkey.dart index b11e84a0..51eb682a 100644 --- a/lib/fragments/hotkey.dart +++ b/lib/fragments/hotkey.dart @@ -146,7 +146,7 @@ class _HotKeyRecorderState extends State { final index = hotKeyActions.indexWhere( (item) => item.key == currentHotkeyAction.key && - keyboardModifiersEquality.equals( + keyboardModifierListEquality.equals( item.modifiers, currentHotkeyAction.modifiers, ), diff --git a/lib/fragments/logs.dart b/lib/fragments/logs.dart index 6bd683c9..30bfb3f1 100644 --- a/lib/fragments/logs.dart +++ b/lib/fragments/logs.dart @@ -1,7 +1,4 @@ import 'dart:async'; -import 'dart:io'; - -import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/state.dart'; @@ -22,7 +19,6 @@ class _LogsFragmentState extends State { final scrollController = ScrollController( keepScrollOffset: false, ); - List> keys = []; Timer? timer; @@ -38,11 +34,8 @@ class _LogsFragmentState extends State { timer = null; } timer = Timer.periodic(const Duration(milliseconds: 200), (timer) { - final maxLength = Platform.isAndroid ? 1000 : 60; - final logs = appFlowingState.logs.safeSublist( - appFlowingState.logs.length - maxLength, - ); - if (!const ListEquality().equals( + final logs = appFlowingState.logs; + if (!logListEquality.equals( logsNotifier.value.logs, logs, )) { @@ -145,15 +138,26 @@ class _LogsFragmentState extends State { child: ValueListenableBuilder( valueListenable: logsNotifier, builder: (_, state, __) { - var logs = state.filteredLogs; + final logs = state.filteredLogs; if (logs.isEmpty) { return NullStatus( label: appLocalizations.nullLogsDesc, ); } - logs = logs.reversed.toList(); - keys = logs - .map((log) => GlobalObjectKey<_LogItemState>(log.dateTime)) + final reversedLogs = logs.reversed.toList(); + final logWidgets = reversedLogs + .map( + (log) => LogItem( + key: Key(log.dateTime.toString()), + log: log, + onClick: _addKeyword, + ), + ) + .separated( + const Divider( + height: 0, + ), + ) .toList(); return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -180,22 +184,38 @@ class _LogsFragmentState extends State { ), ), Expanded( - child: ListView.separated( - controller: scrollController, - itemBuilder: (_, index) { - final log = logs[index]; - return LogItem( - key: Key(log.dateTime.toString()), - log: log, - onClick: _addKeyword, - ); - }, - separatorBuilder: (BuildContext context, int index) { - return const Divider( - height: 0, - ); + child: LayoutBuilder( + builder: (_, constraints) { + return ScrollConfiguration( + behavior: ShowBarScrollBehavior(), + child: ListView.builder( + controller: scrollController, + itemExtentBuilder: (index, __) { + final widget = logWidgets[index]; + if (widget.runtimeType == Divider) { + return 0; + } + final measure = globalState.measure; + final bodyLargeSize = measure.bodyLargeSize; + final bodySmallHeight = measure.bodySmallHeight; + final bodyMediumHeight = measure.bodyMediumHeight; + final log = reversedLogs[(index / 2).floor()]; + final width = (log.payload?.length ?? 0) * + bodyLargeSize.width + + 200; + final lines = (width / constraints.maxWidth).ceil(); + return lines * bodyLargeSize.height + + bodySmallHeight + + 8 + + bodyMediumHeight + + 40; + }, + itemBuilder: (_, index) { + return logWidgets[index]; + }, + itemCount: logWidgets.length, + )); }, - itemCount: logs.length, ), ) ], @@ -365,7 +385,9 @@ class _LogItemState extends State { horizontal: 16, vertical: 4, ), - title: SelectableText(log.payload ?? ''), + title: SelectableText( + log.payload ?? '', + ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/fragments/profiles/profiles.dart b/lib/fragments/profiles/profiles.dart index e4e2b960..86248972 100644 --- a/lib/fragments/profiles/profiles.dart +++ b/lib/fragments/profiles/profiles.dart @@ -459,10 +459,9 @@ class _ReorderableProfilesState extends State { flex: 1, child: ReorderableListView.builder( buildDefaultDragHandles: false, - padding: const EdgeInsets.all(12), + padding: const EdgeInsets.symmetric(horizontal: 12), proxyDecorator: proxyDecorator, - onReorder: (int oldIndex, int newIndex) { - if (oldIndex == newIndex) return; + onReorder: (oldIndex, newIndex) { setState(() { if (oldIndex < newIndex) { newIndex -= 1; diff --git a/lib/fragments/profiles/view_profile.dart b/lib/fragments/profiles/view_profile.dart index e1b42d8a..cf410cdd 100644 --- a/lib/fragments/profiles/view_profile.dart +++ b/lib/fragments/profiles/view_profile.dart @@ -166,20 +166,20 @@ class ContextMenuControllerImpl implements SelectionToolbarController { // _removeOverLayEntry(); } - _handleCut(CodeLineEditingController controller) { - controller.cut(); - _removeOverLayEntry(); - } - - _handleCopy(CodeLineEditingController controller) async { - await controller.copy(); - _removeOverLayEntry(); - } - - _handlePaste(CodeLineEditingController controller) { - controller.paste(); - _removeOverLayEntry(); - } + // _handleCut(CodeLineEditingController controller) { + // controller.cut(); + // _removeOverLayEntry(); + // } + // + // _handleCopy(CodeLineEditingController controller) async { + // await controller.copy(); + // _removeOverLayEntry(); + // } + // + // _handlePaste(CodeLineEditingController controller) { + // controller.paste(); + // _removeOverLayEntry(); + // } @override void show({ diff --git a/lib/fragments/proxies/common.dart b/lib/fragments/proxies/common.dart index 323a7a4f..aac605ac 100644 --- a/lib/fragments/proxies/common.dart +++ b/lib/fragments/proxies/common.dart @@ -81,9 +81,9 @@ double getScrollToSelectedOffset({ final appController = globalState.appController; final columns = other.getProxiesColumns( appController.appState.viewWidth, - appController.config.proxiesLayout, + appController.config.proxiesStyle.layout, ); - final proxyCardType = appController.config.proxyCardType; + final proxyCardType = appController.config.proxiesStyle.cardType; final selectedName = appController.getCurrentSelectedName(groupName); final findSelectedIndex = proxies.indexWhere( (proxy) => proxy.name == selectedName, diff --git a/lib/fragments/proxies/list.dart b/lib/fragments/proxies/list.dart index 08ea675d..c6611956 100644 --- a/lib/fragments/proxies/list.dart +++ b/lib/fragments/proxies/list.dart @@ -1,14 +1,10 @@ import 'dart:math'; -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/state.dart'; -import 'package:fl_clash/widgets/builder.dart'; -import 'package:fl_clash/widgets/card.dart'; -import 'package:fl_clash/widgets/text.dart'; +import 'package:fl_clash/widgets/widgets.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -244,18 +240,17 @@ class _ProxiesListFragmentState extends State { return ProxiesListSelectorState( groupNames: groupNames, currentUnfoldSet: config.currentUnfoldSet, - proxyCardType: config.proxyCardType, - proxiesSortType: config.proxiesSortType, + proxyCardType: config.proxiesStyle.cardType, + proxiesSortType: config.proxiesStyle.sortType, columns: other.getProxiesColumns( appState.viewWidth, - config.proxiesLayout, + config.proxiesStyle.layout, ), sortNum: appState.sortNum, ); }, shouldRebuild: (prev, next) { - if (!const ListEquality() - .equals(prev.groupNames, next.groupNames)) { + if (!stringListEquality.equals(prev.groupNames, next.groupNames)) { _headerStateNotifier.value = const ProxiesListHeaderSelectorState( offset: 0, currentIndex: 0, @@ -264,75 +259,73 @@ class _ProxiesListFragmentState extends State { return prev != next; }, builder: (_, state, __) { - return ScaleBuilder(builder: (_) { - final items = _buildItems( - groupNames: state.groupNames, - currentUnfoldSet: state.currentUnfoldSet, - columns: state.columns, - type: state.proxyCardType, - ); - final itemsOffset = _getItemHeightList(items, state.proxyCardType); - return Scrollbar( - controller: _controller, - thumbVisibility: true, - trackVisibility: true, - thickness: 8, - radius: const Radius.circular(8), - interactive: true, - child: Stack( - children: [ - Positioned.fill( - child: ScrollConfiguration( - behavior: HiddenBarScrollBehavior(), - child: ListView.builder( - padding: const EdgeInsets.all(16), - controller: _controller, - itemExtentBuilder: (index, __) { - return itemsOffset[index]; - }, - itemCount: items.length, - itemBuilder: (_, index) { - return items[index]; - }, - ), + final items = _buildItems( + groupNames: state.groupNames, + currentUnfoldSet: state.currentUnfoldSet, + columns: state.columns, + type: state.proxyCardType, + ); + final itemsOffset = _getItemHeightList(items, state.proxyCardType); + return Scrollbar( + controller: _controller, + thumbVisibility: true, + trackVisibility: true, + thickness: 8, + radius: const Radius.circular(8), + interactive: true, + child: Stack( + children: [ + Positioned.fill( + child: ScrollConfiguration( + behavior: HiddenBarScrollBehavior(), + child: ListView.builder( + padding: const EdgeInsets.all(16), + controller: _controller, + itemExtentBuilder: (index, __) { + return itemsOffset[index]; + }, + itemCount: items.length, + itemBuilder: (_, index) { + return items[index]; + }, ), ), - LayoutBuilder(builder: (_, container) { - return ValueListenableBuilder( - valueListenable: _headerStateNotifier, - builder: (_, headerState, ___) { - final index = - headerState.currentIndex > state.groupNames.length - 1 - ? 0 - : headerState.currentIndex; - return Stack( - children: [ - Positioned( - top: -headerState.offset, - child: Container( - width: container.maxWidth, - color: context.colorScheme.surface, - padding: const EdgeInsets.only( - top: 16, - left: 16, - right: 16, - bottom: 8, - ), - child: _buildHeader( - groupName: state.groupNames[index], - currentUnfoldSet: state.currentUnfoldSet, - ), + ), + LayoutBuilder(builder: (_, container) { + return ValueListenableBuilder( + valueListenable: _headerStateNotifier, + builder: (_, headerState, ___) { + final index = + headerState.currentIndex > state.groupNames.length - 1 + ? 0 + : headerState.currentIndex; + return Stack( + children: [ + Positioned( + top: -headerState.offset, + child: Container( + width: container.maxWidth, + color: context.colorScheme.surface, + padding: const EdgeInsets.only( + top: 16, + left: 16, + right: 16, + bottom: 8, + ), + child: _buildHeader( + groupName: state.groupNames[index], + currentUnfoldSet: state.currentUnfoldSet, ), ), - ], - ); - }, - ); - }), - ], - ), - ); - }); + ), + ], + ); + }, + ); + }), + ], + ), + ); }, ); } @@ -379,11 +372,6 @@ class _ListHeaderState extends State } _handleChange(String groupName) { - if (isExpand) { - _animationController.reverse(); - } else { - _animationController.forward(); - } widget.onChange(groupName); } @@ -413,13 +401,69 @@ class _ListHeaderState extends State super.didUpdateWidget(oldWidget); if (oldWidget.isExpand != widget.isExpand) { if (isExpand) { - _animationController.value = 1.0; + _animationController.forward(); } else { - _animationController.value = 0.0; + _animationController.reverse(); } } } + Widget _buildIcon() { + return Selector( + selector: (_, config) => config.proxiesStyle.iconStyle, + builder: (_, iconStyle, child) { + return Selector( + selector: (_, config) { + final iconMapEntryList = + config.proxiesStyle.iconMap.entries.toList(); + final index = iconMapEntryList.indexWhere((item) { + try{ + return RegExp(item.key).hasMatch(groupName); + }catch(_){ + return false; + } + }); + if (index != -1) { + return iconMapEntryList[index].value; + } + return icon; + }, + builder: (_, icon, __) { + return switch (iconStyle) { + ProxiesIconStyle.standard => Container( + height: 48, + width: 48, + margin: const EdgeInsets.only( + right: 16, + ), + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: context.colorScheme.secondaryContainer, + borderRadius: BorderRadius.circular(16), + ), + clipBehavior: Clip.antiAlias, + child: CommonIcon( + src: icon, + size: 32, + ), + ), + ProxiesIconStyle.icon => Container( + margin: const EdgeInsets.only( + right: 16, + ), + child: CommonIcon( + src: icon, + size: 42, + ), + ), + ProxiesIconStyle.none => Container(), + }; + }, + ); + }, + ); + } + @override Widget build(BuildContext context) { return CommonCard( @@ -427,41 +471,17 @@ class _ListHeaderState extends State radius: 24, type: CommonCardType.filled, child: Container( - padding: const EdgeInsets.all(12), + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: Row( children: [ - const SizedBox( - width: 4, - ), - Container( - height: 48, - width: 48, - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: context.colorScheme.secondaryContainer, - borderRadius: BorderRadius.circular(16), - ), - clipBehavior: Clip.antiAlias, - child: icon.isNotEmpty - ? CachedNetworkImage( - imageUrl: icon, - errorWidget: (_, __, ___) => const Icon( - IconsExt.target, - size: 32, - ), - ) - : const Icon( - IconsExt.target, - size: 32, - ), - ), - const SizedBox( - width: 16, - ), + _buildIcon(), Flexible( child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/fragments/proxies/proxies.dart b/lib/fragments/proxies/proxies.dart index 05723777..87b614fb 100644 --- a/lib/fragments/proxies/proxies.dart +++ b/lib/fragments/proxies/proxies.dart @@ -1,11 +1,11 @@ -import 'package:fl_clash/common/app_localizations.dart'; +import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/fragments/proxies/list.dart'; import 'package:fl_clash/models/models.dart'; +import 'package:fl_clash/state.dart'; import 'package:fl_clash/widgets/widgets.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; - import 'providers.dart'; import 'setting.dart'; import 'tab.dart'; @@ -37,7 +37,7 @@ class _ProxiesFragmentState extends State { ); }, icon: const Icon( - Icons.swap_vert_circle_outlined, + Icons.poll_outlined, ), ), const SizedBox( @@ -56,6 +56,63 @@ class _ProxiesFragmentState extends State { const SizedBox( width: 8, ) + ] else ...[ + IconButton( + onPressed: () { + showExtendPage( + context, + extendPageWidth: 360, + title: appLocalizations.iconConfiguration, + body: Selector>( + selector: (_, config) => config.proxiesStyle.iconMap, + shouldRebuild: (prev, next) { + return !stringAndStringMapEntryIterableEquality.equals( + prev.entries, + next.entries, + ); + }, + builder: (_, iconMap, __) { + final entries = iconMap.entries.toList(); + return ListPage( + title: appLocalizations.iconConfiguration, + items: entries, + keyLabel: appLocalizations.regExp, + valueLabel: appLocalizations.icon, + keyBuilder: (item) => Key(item.key), + titleBuilder: (item) => Text(item.key), + leadingBuilder: (item) => Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + ), + clipBehavior: Clip.antiAlias, + child: CommonIcon( + src: item.value, + size: 42, + ), + ), + subtitleBuilder: (item) => Text( + item.value, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + onChange: (entries) { + final config = globalState.appController.config; + config.proxiesStyle = config.proxiesStyle.copyWith( + iconMap: Map.fromEntries(entries), + ); + }, + ); + }, + ), + ); + }, + icon: const Icon( + Icons.style_outlined, + ), + ), + const SizedBox( + width: 8, + ) ], IconButton( onPressed: () { @@ -63,7 +120,7 @@ class _ProxiesFragmentState extends State { title: appLocalizations.proxiesSetting, context: context, builder: (context) { - return const ProxiesSettingWidget(); + return const ProxiesSetting(); }, ); }, @@ -78,7 +135,7 @@ class _ProxiesFragmentState extends State { @override Widget build(BuildContext context) { return Selector( - selector: (_, config) => config.proxiesType, + selector: (_, config) => config.proxiesStyle.type, builder: (_, proxiesType, __) { return ProxiesActionsBuilder( builder: (state, child) { diff --git a/lib/fragments/proxies/setting.dart b/lib/fragments/proxies/setting.dart index 21606287..9ed87413 100644 --- a/lib/fragments/proxies/setting.dart +++ b/lib/fragments/proxies/setting.dart @@ -7,8 +7,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -class ProxiesSettingWidget extends StatelessWidget { - const ProxiesSettingWidget({super.key}); +class ProxiesSetting extends StatelessWidget { + const ProxiesSetting({super.key}); IconData _getIconWithProxiesType(ProxiesType type) { return switch (type) { @@ -41,6 +41,14 @@ class ProxiesSettingWidget extends StatelessWidget { }; } + String _getTextWithProxiesIconStyle(ProxiesIconStyle style) { + return switch (style) { + ProxiesIconStyle.standard => appLocalizations.standard, + ProxiesIconStyle.none => appLocalizations.noIcon, + ProxiesIconStyle.icon => appLocalizations.onlyIcon, + }; + } + List _buildStyleSetting() { return generateSection( title: appLocalizations.style, @@ -49,7 +57,7 @@ class ProxiesSettingWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16), scrollDirection: Axis.horizontal, child: Selector( - selector: (_, config) => config.proxiesType, + selector: (_, config) => config.proxiesStyle.type, builder: (_, proxiesType, __) { final config = globalState.appController.config; return Wrap( @@ -63,7 +71,9 @@ class ProxiesSettingWidget extends StatelessWidget { ), isSelected: proxiesType == item, onPressed: () { - config.proxiesType = item; + config.proxiesStyle = config.proxiesStyle.copyWith( + type: item, + ); }, ) ], @@ -83,7 +93,7 @@ class ProxiesSettingWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16), scrollDirection: Axis.horizontal, child: Selector( - selector: (_, config) => config.proxiesSortType, + selector: (_, config) => config.proxiesStyle.sortType, builder: (_, proxiesSortType, __) { final config = globalState.appController.config; return Wrap( @@ -97,7 +107,9 @@ class ProxiesSettingWidget extends StatelessWidget { ), isSelected: proxiesSortType == item, onPressed: () { - config.proxiesSortType = item; + config.proxiesStyle = config.proxiesStyle.copyWith( + sortType: item, + ); }, ), ], @@ -117,7 +129,7 @@ class ProxiesSettingWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16), scrollDirection: Axis.horizontal, child: Selector( - selector: (_, config) => config.proxyCardType, + selector: (_, config) => config.proxiesStyle.cardType, builder: (_, proxyCardType, __) { final config = globalState.appController.config; return Wrap( @@ -128,7 +140,9 @@ class ProxiesSettingWidget extends StatelessWidget { Intl.message(item.name), isSelected: item == proxyCardType, onPressed: () { - config.proxyCardType = item; + config.proxiesStyle = config.proxiesStyle.copyWith( + cardType: item, + ); }, ) ], @@ -149,8 +163,8 @@ class ProxiesSettingWidget extends StatelessWidget { horizontal: 16, ), scrollDirection: Axis.horizontal, - child: Selector< Config, ProxiesLayout>( - selector: (_, config) => config.proxiesLayout, + child: Selector( + selector: (_, config) => config.proxiesStyle.layout, builder: (_, proxiesLayout, __) { final config = globalState.appController.config; return Wrap( @@ -161,7 +175,9 @@ class ProxiesSettingWidget extends StatelessWidget { getTextForProxiesLayout(item), isSelected: item == proxiesLayout, onPressed: () { - config.proxiesLayout = item; + config.proxiesStyle = config.proxiesStyle.copyWith( + layout: item, + ); }, ) ], @@ -173,6 +189,39 @@ class ProxiesSettingWidget extends StatelessWidget { ); } + _buildGroupStyleSetting() { + return generateSection( + title: "图标样式", + items: [ + SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16), + scrollDirection: Axis.horizontal, + child: Selector( + selector: (_, config) => config.proxiesStyle.iconStyle, + builder: (_, iconStyle, __) { + return Wrap( + spacing: 16, + children: [ + for (final item in ProxiesIconStyle.values) + SettingTextCard( + _getTextWithProxiesIconStyle(item), + isSelected: iconStyle == item, + onPressed: () { + final config = globalState.appController.config; + config.proxiesStyle = config.proxiesStyle.copyWith( + iconStyle: item, + ); + }, + ), + ], + ); + }, + ), + ), + ], + ); + } + @override Widget build(BuildContext context) { return Padding( @@ -185,6 +234,22 @@ class ProxiesSettingWidget extends StatelessWidget { ..._buildSortSetting(), ..._buildLayoutSetting(), ..._buildSizeSetting(), + Selector( + selector: (_, config) => + config.proxiesStyle.type == ProxiesType.list, + builder: (_, value, child) { + if (value) { + return child!; + } + return Container(); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ..._buildGroupStyleSetting(), + ], + ), + ), ], ), ); diff --git a/lib/fragments/proxies/tab.dart b/lib/fragments/proxies/tab.dart index d65cd883..8c59d59c 100644 --- a/lib/fragments/proxies/tab.dart +++ b/lib/fragments/proxies/tab.dart @@ -1,6 +1,4 @@ import 'dart:math'; - -import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; @@ -120,8 +118,7 @@ class ProxiesTabFragmentState extends State ); }, shouldRebuild: (prev, next) { - if (!const ListEquality() - .equals(prev.groupNames, next.groupNames)) { + if (!stringListEquality.equals(prev.groupNames, next.groupNames)) { _tabController?.dispose(); _tabController = null; return true; @@ -287,11 +284,11 @@ class ProxyGroupViewState extends State { selector: (_, appState, config) { final group = appState.getGroupWithName(groupName)!; return ProxyGroupSelectorState( - proxyCardType: config.proxyCardType, - proxiesSortType: config.proxiesSortType, + proxyCardType: config.proxiesStyle.cardType, + proxiesSortType: config.proxiesStyle.sortType, columns: other.getProxiesColumns( appState.viewWidth, - config.proxiesLayout, + config.proxiesStyle.layout, ), sortNum: appState.sortNum, proxies: group.all, @@ -314,33 +311,31 @@ class ProxyGroupViewState extends State { }, child: Align( alignment: Alignment.topCenter, - child: ScaleBuilder( - builder: (_) => GridView.builder( - controller: _controller, - padding: const EdgeInsets.only( - top: 16, - left: 16, - right: 16, - bottom: 96, - ), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: columns, - mainAxisSpacing: 8, - crossAxisSpacing: 8, - mainAxisExtent: getItemHeight(proxyCardType), - ), - itemCount: sortedProxies.length, - itemBuilder: (_, index) { - final proxy = sortedProxies[index]; - return ProxyCard( - groupType: state.groupType, - type: proxyCardType, - key: ValueKey('$groupName.${proxy.name}'), - proxy: proxy, - groupName: groupName, - ); - }, + child: GridView.builder( + controller: _controller, + padding: const EdgeInsets.only( + top: 16, + left: 16, + right: 16, + bottom: 96, + ), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: columns, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + mainAxisExtent: getItemHeight(proxyCardType), ), + itemCount: sortedProxies.length, + itemBuilder: (_, index) { + final proxy = sortedProxies[index]; + return ProxyCard( + groupType: state.groupType, + type: proxyCardType, + key: ValueKey('$groupName.${proxy.name}'), + proxy: proxy, + groupName: groupName, + ); + }, ), ), ); diff --git a/lib/fragments/requests.dart b/lib/fragments/requests.dart index 95fdbf3b..328ae9eb 100644 --- a/lib/fragments/requests.dart +++ b/lib/fragments/requests.dart @@ -1,7 +1,5 @@ import 'dart:async'; import 'dart:io'; - -import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; @@ -42,7 +40,7 @@ class _RequestsFragmentState extends State { final requests = appState.requests.safeSublist( appState.requests.length - maxLength, ); - if (!const ListEquality().equals( + if (!connectionListEquality.equals( requestsNotifier.value.connections, requests, )) { diff --git a/lib/fragments/tools.dart b/lib/fragments/tools.dart index ac230ad8..ef5031ea 100644 --- a/lib/fragments/tools.dart +++ b/lib/fragments/tools.dart @@ -91,7 +91,7 @@ class _ToolboxFragmentState extends State { title: appLocalizations.settings, items: [ Selector( - selector: (_, config) => config.locale, + selector: (_, config) => config.appSetting.locale, builder: (_, localeString, __) { final subTitle = localeString ?? appLocalizations.defaultText; final currentLocale = other.getLocaleForString(localeString); @@ -103,8 +103,10 @@ class _ToolboxFragmentState extends State { title: appLocalizations.language, options: [null, ...AppLocalizations.delegate.supportedLocales], onChanged: (Locale? value) { - final config = context.read(); - config.locale = value?.toString(); + final config = globalState.appController.config; + config.appSetting = config.appSetting.copyWith( + locale: value?.toString(), + ); }, textBuilder: (locale) => _getLocaleString(locale), value: currentLocale, diff --git a/lib/l10n/arb/intl_en.arb b/lib/l10n/arb/intl_en.arb index 086ba147..4d70ca96 100644 --- a/lib/l10n/arb/intl_en.arb +++ b/lib/l10n/arb/intl_en.arb @@ -41,7 +41,7 @@ "tunDesc": "only effective in administrator mode", "minimizeOnExit": "Minimize on exit", "minimizeOnExitDesc": "Modify the default system exit event", - "autoLaunch": "AutoLaunch", + "autoLaunch": "Auto launch", "autoLaunchDesc": "Follow the system self startup", "silentLaunch": "SilentLaunch", "silentLaunchDesc": "Start in the background", @@ -250,8 +250,7 @@ "dnsDesc": "Update DNS related settings", "key": "Key", "value": "Value", - "keyNotEmpty": "The key cannot be empty", - "valueNotEmpty": "The value cannot be empty", + "notEmpty": "Cannot be empty", "hostsDesc": "Add Hosts", "vpnTip": "Changes take effect after restarting the VPN", "vpnEnableDesc": "Auto routes all system traffic through VpnService", @@ -287,7 +286,6 @@ "geoipCode": "Geoip code", "ipcidr": "Ipcidr", "domain": "Domain", - "resetDns": "Reset Dns", "reset": "Reset", "action_view": "Show/Hide", "action_start": "Start/Stop", @@ -304,9 +302,23 @@ "hotkeyConflict": "Hotkey conflict", "remove": "Remove", "noHotKey": "No HotKey", - "dnsResetTip": "Make sure to reset the DNS", "noNetwork": "No network", "ipv6InboundDesc": "Allow IPv6 inbound", "exportLogs": "Export logs", - "exportSuccess": "Export Success" + "exportSuccess": "Export Success", + "iconStyle": "Icon style", + "onlyIcon": "Icon", + "noIcon": "None", + "stackMode": "Stack mode", + "network": "Network", + "networkDesc": "Modify network-related settings", + "bypassDomain": "Bypass domain", + "bypassDomainDesc": "Only takes effect when the system proxy is enabled", + "resetTip": "Make sure to reset", + "regExp": "RegExp", + "icon": "Icon", + "iconConfiguration": "Icon configuration", + "noData": "No data", + "adminAutoLaunch": "Admin auto launch", + "adminAutoLaunchDesc": "Boot up by using admin mode" } \ No newline at end of file diff --git a/lib/l10n/arb/intl_zh_CN.arb b/lib/l10n/arb/intl_zh_CN.arb index 188eea86..acb6a36b 100644 --- a/lib/l10n/arb/intl_zh_CN.arb +++ b/lib/l10n/arb/intl_zh_CN.arb @@ -250,8 +250,7 @@ "dnsDesc": "更新DNS相关设置", "key": "键", "value": "值", - "keyNotEmpty": "键不能为空", - "valueNotEmpty": "值不能为空", + "notEmpty": "不能为空", "hostsDesc": "追加Hosts", "vpnTip": "重启VPN后改变生效", "vpnEnableDesc": "通过VpnService自动路由系统所有流量", @@ -287,7 +286,6 @@ "geoipCode": "Geoip代码", "ipcidr": "IP/掩码", "domain": "域名", - "resetDns": "重置DNS", "reset": "重置", "action_view": "显示/隐藏", "action_start": "启动/停止", @@ -304,9 +302,23 @@ "hotkeyConflict": "快捷键冲突", "remove": "移除", "noHotKey": "暂无快捷键", - "dnsResetTip": "确定重置DNS", "noNetwork": "无网络", "ipv6InboundDesc": "允许IPv6入站", "exportLogs": "导出日志", - "exportSuccess": "导出成功" + "exportSuccess": "导出成功", + "iconStyle": "图标样式", + "onlyIcon": "仅图标", + "noIcon": "无图标", + "stackMode": "栈模式", + "network": "网络", + "networkDesc": "修改网络相关设置", + "bypassDomain": "排除域名", + "bypassDomainDesc": "仅在系统代理启用时生效", + "resetTip": "确定要重置吗?", + "regExp": "正则", + "icon": "图片", + "iconConfiguration": "图片配置", + "noData": "暂无数据", + "adminAutoLaunch": "管理员自启动", + "adminAutoLaunchDesc": "使用管理员模式开机自启动" } \ No newline at end of file diff --git a/lib/l10n/intl/messages_en.dart b/lib/l10n/intl/messages_en.dart index 3bff0cfb..305dd7d0 100644 --- a/lib/l10n/intl/messages_en.dart +++ b/lib/l10n/intl/messages_en.dart @@ -45,6 +45,10 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("WebDAV server address"), "addressTip": MessageLookupByLibrary.simpleMessage( "Please enter a valid WebDAV address"), + "adminAutoLaunch": + MessageLookupByLibrary.simpleMessage("Admin auto launch"), + "adminAutoLaunchDesc": + MessageLookupByLibrary.simpleMessage("Boot up by using admin mode"), "ago": MessageLookupByLibrary.simpleMessage(" Ago"), "agree": MessageLookupByLibrary.simpleMessage("Agree"), "allApps": MessageLookupByLibrary.simpleMessage("All apps"), @@ -72,7 +76,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Auto lose connections"), "autoCloseConnectionsDesc": MessageLookupByLibrary.simpleMessage( "Auto close connections after change node"), - "autoLaunch": MessageLookupByLibrary.simpleMessage("AutoLaunch"), + "autoLaunch": MessageLookupByLibrary.simpleMessage("Auto launch"), "autoLaunchDesc": MessageLookupByLibrary.simpleMessage( "Follow the system self startup"), "autoRun": MessageLookupByLibrary.simpleMessage("AutoRun"), @@ -89,6 +93,9 @@ class MessageLookup extends MessageLookupByLibrary { "backupSuccess": MessageLookupByLibrary.simpleMessage("Backup success"), "bind": MessageLookupByLibrary.simpleMessage("Bind"), "blacklistMode": MessageLookupByLibrary.simpleMessage("Blacklist mode"), + "bypassDomain": MessageLookupByLibrary.simpleMessage("Bypass domain"), + "bypassDomainDesc": MessageLookupByLibrary.simpleMessage( + "Only takes effect when the system proxy is enabled"), "cancelFilterSystemApp": MessageLookupByLibrary.simpleMessage("Cancel filter system app"), "cancelSelectAll": @@ -146,8 +153,6 @@ class MessageLookup extends MessageLookupByLibrary { "dnsDesc": MessageLookupByLibrary.simpleMessage("Update DNS related settings"), "dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"), - "dnsResetTip": - MessageLookupByLibrary.simpleMessage("Make sure to reset the DNS"), "doYouWantToPass": MessageLookupByLibrary.simpleMessage("Do you want to pass"), "domain": MessageLookupByLibrary.simpleMessage("Domain"), @@ -208,6 +213,10 @@ class MessageLookup extends MessageLookupByLibrary { "hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage( "Use keyboard to control applications"), "hours": MessageLookupByLibrary.simpleMessage("Hours"), + "icon": MessageLookupByLibrary.simpleMessage("Icon"), + "iconConfiguration": + MessageLookupByLibrary.simpleMessage("Icon configuration"), + "iconStyle": MessageLookupByLibrary.simpleMessage("Icon style"), "importFromURL": MessageLookupByLibrary.simpleMessage("Import from URL"), "infiniteTime": @@ -227,8 +236,6 @@ class MessageLookup extends MessageLookupByLibrary { "keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage("Tcp keep alive interval"), "key": MessageLookupByLibrary.simpleMessage("Key"), - "keyNotEmpty": - MessageLookupByLibrary.simpleMessage("The key cannot be empty"), "language": MessageLookupByLibrary.simpleMessage("Language"), "layout": MessageLookupByLibrary.simpleMessage("Layout"), "light": MessageLookupByLibrary.simpleMessage("Light"), @@ -267,16 +274,22 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Nameserver policy"), "nameserverPolicyDesc": MessageLookupByLibrary.simpleMessage( "Specify the corresponding nameserver policy"), + "network": MessageLookupByLibrary.simpleMessage("Network"), + "networkDesc": MessageLookupByLibrary.simpleMessage( + "Modify network-related settings"), "networkDetection": MessageLookupByLibrary.simpleMessage("Network detection"), "networkSpeed": MessageLookupByLibrary.simpleMessage("Network speed"), + "noData": MessageLookupByLibrary.simpleMessage("No data"), "noHotKey": MessageLookupByLibrary.simpleMessage("No HotKey"), + "noIcon": MessageLookupByLibrary.simpleMessage("None"), "noInfo": MessageLookupByLibrary.simpleMessage("No info"), "noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("No more info"), "noNetwork": MessageLookupByLibrary.simpleMessage("No network"), "noProxy": MessageLookupByLibrary.simpleMessage("No proxy"), "noProxyDesc": MessageLookupByLibrary.simpleMessage( "Please create a profile or add a valid profile"), + "notEmpty": MessageLookupByLibrary.simpleMessage("Cannot be empty"), "notSelectedTip": MessageLookupByLibrary.simpleMessage( "The current proxy group cannot be selected."), "nullConnectionsDesc": @@ -288,6 +301,7 @@ class MessageLookup extends MessageLookupByLibrary { "No profile, Please add a profile"), "nullRequestsDesc": MessageLookupByLibrary.simpleMessage("No requests"), "oneColumn": MessageLookupByLibrary.simpleMessage("One column"), + "onlyIcon": MessageLookupByLibrary.simpleMessage("Icon"), "onlyOtherApps": MessageLookupByLibrary.simpleMessage("Only third-party apps"), "onlyStatisticsProxy": @@ -365,6 +379,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Only recovery profiles"), "recoverySuccess": MessageLookupByLibrary.simpleMessage("Recovery success"), + "regExp": MessageLookupByLibrary.simpleMessage("RegExp"), "remote": MessageLookupByLibrary.simpleMessage("Remote"), "remoteBackupDesc": MessageLookupByLibrary.simpleMessage("Backup local data to WebDAV"), @@ -375,7 +390,7 @@ class MessageLookup extends MessageLookupByLibrary { "requestsDesc": MessageLookupByLibrary.simpleMessage( "View recently request records"), "reset": MessageLookupByLibrary.simpleMessage("Reset"), - "resetDns": MessageLookupByLibrary.simpleMessage("Reset Dns"), + "resetTip": MessageLookupByLibrary.simpleMessage("Make sure to reset"), "resources": MessageLookupByLibrary.simpleMessage("Resources"), "resourcesDesc": MessageLookupByLibrary.simpleMessage( "External resource related info"), @@ -398,6 +413,7 @@ class MessageLookup extends MessageLookupByLibrary { "size": MessageLookupByLibrary.simpleMessage("Size"), "sort": MessageLookupByLibrary.simpleMessage("Sort"), "source": MessageLookupByLibrary.simpleMessage("Source"), + "stackMode": MessageLookupByLibrary.simpleMessage("Stack mode"), "standard": MessageLookupByLibrary.simpleMessage("Standard"), "start": MessageLookupByLibrary.simpleMessage("Start"), "startVpn": MessageLookupByLibrary.simpleMessage("Starting VPN..."), @@ -451,8 +467,6 @@ class MessageLookup extends MessageLookupByLibrary { "useSystemHosts": MessageLookupByLibrary.simpleMessage("Use system hosts"), "value": MessageLookupByLibrary.simpleMessage("Value"), - "valueNotEmpty": - MessageLookupByLibrary.simpleMessage("The value cannot be empty"), "view": MessageLookupByLibrary.simpleMessage("View"), "vpnDesc": MessageLookupByLibrary.simpleMessage("Modify VPN related settings"), diff --git a/lib/l10n/intl/messages_zh_CN.dart b/lib/l10n/intl/messages_zh_CN.dart index e2338e2b..bef12117 100644 --- a/lib/l10n/intl/messages_zh_CN.dart +++ b/lib/l10n/intl/messages_zh_CN.dart @@ -41,6 +41,9 @@ class MessageLookup extends MessageLookupByLibrary { "address": MessageLookupByLibrary.simpleMessage("地址"), "addressHelp": MessageLookupByLibrary.simpleMessage("WebDAV服务器地址"), "addressTip": MessageLookupByLibrary.simpleMessage("请输入有效的WebDAV地址"), + "adminAutoLaunch": MessageLookupByLibrary.simpleMessage("管理员自启动"), + "adminAutoLaunchDesc": + MessageLookupByLibrary.simpleMessage("使用管理员模式开机自启动"), "ago": MessageLookupByLibrary.simpleMessage("前"), "agree": MessageLookupByLibrary.simpleMessage("同意"), "allApps": MessageLookupByLibrary.simpleMessage("所有应用"), @@ -75,6 +78,8 @@ class MessageLookup extends MessageLookupByLibrary { "backupSuccess": MessageLookupByLibrary.simpleMessage("备份成功"), "bind": MessageLookupByLibrary.simpleMessage("绑定"), "blacklistMode": MessageLookupByLibrary.simpleMessage("黑名单模式"), + "bypassDomain": MessageLookupByLibrary.simpleMessage("排除域名"), + "bypassDomainDesc": MessageLookupByLibrary.simpleMessage("仅在系统代理启用时生效"), "cancelFilterSystemApp": MessageLookupByLibrary.simpleMessage("取消过滤系统应用"), "cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"), @@ -120,7 +125,6 @@ class MessageLookup extends MessageLookupByLibrary { "discovery": MessageLookupByLibrary.simpleMessage("发现新版本"), "dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"), "dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"), - "dnsResetTip": MessageLookupByLibrary.simpleMessage("确定重置DNS"), "doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"), "domain": MessageLookupByLibrary.simpleMessage("域名"), "download": MessageLookupByLibrary.simpleMessage("下载"), @@ -168,6 +172,9 @@ class MessageLookup extends MessageLookupByLibrary { "hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage("使用键盘控制应用程序"), "hours": MessageLookupByLibrary.simpleMessage("小时"), + "icon": MessageLookupByLibrary.simpleMessage("图片"), + "iconConfiguration": MessageLookupByLibrary.simpleMessage("图片配置"), + "iconStyle": MessageLookupByLibrary.simpleMessage("图标样式"), "importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"), "infiniteTime": MessageLookupByLibrary.simpleMessage("长期有效"), "init": MessageLookupByLibrary.simpleMessage("初始化"), @@ -181,7 +188,6 @@ class MessageLookup extends MessageLookupByLibrary { "keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"), "key": MessageLookupByLibrary.simpleMessage("键"), - "keyNotEmpty": MessageLookupByLibrary.simpleMessage("键不能为空"), "language": MessageLookupByLibrary.simpleMessage("语言"), "layout": MessageLookupByLibrary.simpleMessage("布局"), "light": MessageLookupByLibrary.simpleMessage("浅色"), @@ -212,15 +218,20 @@ class MessageLookup extends MessageLookupByLibrary { "nameserverPolicy": MessageLookupByLibrary.simpleMessage("域名服务器策略"), "nameserverPolicyDesc": MessageLookupByLibrary.simpleMessage("指定对应域名服务器策略"), + "network": MessageLookupByLibrary.simpleMessage("网络"), + "networkDesc": MessageLookupByLibrary.simpleMessage("修改网络相关设置"), "networkDetection": MessageLookupByLibrary.simpleMessage("网络检测"), "networkSpeed": MessageLookupByLibrary.simpleMessage("网络速度"), + "noData": MessageLookupByLibrary.simpleMessage("暂无数据"), "noHotKey": MessageLookupByLibrary.simpleMessage("暂无快捷键"), + "noIcon": MessageLookupByLibrary.simpleMessage("无图标"), "noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"), "noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("暂无更多信息"), "noNetwork": MessageLookupByLibrary.simpleMessage("无网络"), "noProxy": MessageLookupByLibrary.simpleMessage("暂无代理"), "noProxyDesc": MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"), + "notEmpty": MessageLookupByLibrary.simpleMessage("不能为空"), "notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"), "nullConnectionsDesc": MessageLookupByLibrary.simpleMessage("暂无连接"), "nullCoreInfoDesc": MessageLookupByLibrary.simpleMessage("无法获取内核信息"), @@ -229,6 +240,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("没有配置文件,请先添加配置文件"), "nullRequestsDesc": MessageLookupByLibrary.simpleMessage("暂无请求"), "oneColumn": MessageLookupByLibrary.simpleMessage("一列"), + "onlyIcon": MessageLookupByLibrary.simpleMessage("仅图标"), "onlyOtherApps": MessageLookupByLibrary.simpleMessage("仅第三方应用"), "onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage("仅统计代理"), "onlyStatisticsProxyDesc": @@ -286,6 +298,7 @@ class MessageLookup extends MessageLookupByLibrary { "recoveryAll": MessageLookupByLibrary.simpleMessage("恢复所有数据"), "recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"), "recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"), + "regExp": MessageLookupByLibrary.simpleMessage("正则"), "remote": MessageLookupByLibrary.simpleMessage("远程"), "remoteBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到WebDAV"), "remoteRecoveryDesc": @@ -294,7 +307,7 @@ class MessageLookup extends MessageLookupByLibrary { "requests": MessageLookupByLibrary.simpleMessage("请求"), "requestsDesc": MessageLookupByLibrary.simpleMessage("查看最近请求记录"), "reset": MessageLookupByLibrary.simpleMessage("重置"), - "resetDns": MessageLookupByLibrary.simpleMessage("重置DNS"), + "resetTip": MessageLookupByLibrary.simpleMessage("确定要重置吗?"), "resources": MessageLookupByLibrary.simpleMessage("资源"), "resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"), "respectRules": MessageLookupByLibrary.simpleMessage("遵守规则"), @@ -315,6 +328,7 @@ class MessageLookup extends MessageLookupByLibrary { "size": MessageLookupByLibrary.simpleMessage("尺寸"), "sort": MessageLookupByLibrary.simpleMessage("排序"), "source": MessageLookupByLibrary.simpleMessage("来源"), + "stackMode": MessageLookupByLibrary.simpleMessage("栈模式"), "standard": MessageLookupByLibrary.simpleMessage("标准"), "start": MessageLookupByLibrary.simpleMessage("启动"), "startVpn": MessageLookupByLibrary.simpleMessage("正在启动VPN..."), @@ -360,7 +374,6 @@ class MessageLookup extends MessageLookupByLibrary { "useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"), "useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"), "value": MessageLookupByLibrary.simpleMessage("值"), - "valueNotEmpty": MessageLookupByLibrary.simpleMessage("值不能为空"), "view": MessageLookupByLibrary.simpleMessage("查看"), "vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"), "vpnEnableDesc": diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 0691d1a1..e837ff21 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -470,10 +470,10 @@ class AppLocalizations { ); } - /// `AutoLaunch` + /// `Auto launch` String get autoLaunch { return Intl.message( - 'AutoLaunch', + 'Auto launch', name: 'autoLaunch', desc: '', args: [], @@ -2560,21 +2560,11 @@ class AppLocalizations { ); } - /// `The key cannot be empty` - String get keyNotEmpty { + /// `Cannot be empty` + String get notEmpty { return Intl.message( - 'The key cannot be empty', - name: 'keyNotEmpty', - desc: '', - args: [], - ); - } - - /// `The value cannot be empty` - String get valueNotEmpty { - return Intl.message( - 'The value cannot be empty', - name: 'valueNotEmpty', + 'Cannot be empty', + name: 'notEmpty', desc: '', args: [], ); @@ -2930,16 +2920,6 @@ class AppLocalizations { ); } - /// `Reset Dns` - String get resetDns { - return Intl.message( - 'Reset Dns', - name: 'resetDns', - desc: '', - args: [], - ); - } - /// `Reset` String get reset { return Intl.message( @@ -3100,16 +3080,6 @@ class AppLocalizations { ); } - /// `Make sure to reset the DNS` - String get dnsResetTip { - return Intl.message( - 'Make sure to reset the DNS', - name: 'dnsResetTip', - desc: '', - args: [], - ); - } - /// `No network` String get noNetwork { return Intl.message( @@ -3149,6 +3119,156 @@ class AppLocalizations { args: [], ); } + + /// `Icon style` + String get iconStyle { + return Intl.message( + 'Icon style', + name: 'iconStyle', + desc: '', + args: [], + ); + } + + /// `Icon` + String get onlyIcon { + return Intl.message( + 'Icon', + name: 'onlyIcon', + desc: '', + args: [], + ); + } + + /// `None` + String get noIcon { + return Intl.message( + 'None', + name: 'noIcon', + desc: '', + args: [], + ); + } + + /// `Stack mode` + String get stackMode { + return Intl.message( + 'Stack mode', + name: 'stackMode', + desc: '', + args: [], + ); + } + + /// `Network` + String get network { + return Intl.message( + 'Network', + name: 'network', + desc: '', + args: [], + ); + } + + /// `Modify network-related settings` + String get networkDesc { + return Intl.message( + 'Modify network-related settings', + name: 'networkDesc', + desc: '', + args: [], + ); + } + + /// `Bypass domain` + String get bypassDomain { + return Intl.message( + 'Bypass domain', + name: 'bypassDomain', + desc: '', + args: [], + ); + } + + /// `Only takes effect when the system proxy is enabled` + String get bypassDomainDesc { + return Intl.message( + 'Only takes effect when the system proxy is enabled', + name: 'bypassDomainDesc', + desc: '', + args: [], + ); + } + + /// `Make sure to reset` + String get resetTip { + return Intl.message( + 'Make sure to reset', + name: 'resetTip', + desc: '', + args: [], + ); + } + + /// `RegExp` + String get regExp { + return Intl.message( + 'RegExp', + name: 'regExp', + desc: '', + args: [], + ); + } + + /// `Icon` + String get icon { + return Intl.message( + 'Icon', + name: 'icon', + desc: '', + args: [], + ); + } + + /// `Icon configuration` + String get iconConfiguration { + return Intl.message( + 'Icon configuration', + name: 'iconConfiguration', + desc: '', + args: [], + ); + } + + /// `No data` + String get noData { + return Intl.message( + 'No data', + name: 'noData', + desc: '', + args: [], + ); + } + + /// `Admin auto launch` + String get adminAutoLaunch { + return Intl.message( + 'Admin auto launch', + name: 'adminAutoLaunch', + desc: '', + args: [], + ); + } + + /// `Boot up by using admin mode` + String get adminAutoLaunchDesc { + return Intl.message( + 'Boot up by using admin mode', + name: 'adminAutoLaunchDesc', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/main.dart b/lib/main.dart index db058fdf..173a8fb6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:io'; -import 'dart:isolate'; import 'package:fl_clash/clash/clash.dart'; import 'package:fl_clash/plugins/app.dart'; @@ -20,18 +19,16 @@ Future main() async { globalState.packageInfo = await PackageInfo.fromPlatform(); final version = await system.version; final config = await preferences.getConfig() ?? Config(); - globalState.autoRun = config.autoRun; final clashConfig = await preferences.getClashConfig() ?? ClashConfig(); await android?.init(); await window?.init(config.windowProps, version); final appState = AppState( mode: clashConfig.mode, version: version, - isCompatible: config.isCompatible, selectedMap: config.currentSelectedMap, ); appState.navigationItems = navigation.getItems( - openLogs: config.openLogs, + openLogs: config.appSetting.openLogs, hasProxies: false, ); await globalState.init( @@ -58,7 +55,6 @@ Future vpnService() async { final clashConfig = await preferences.getClashConfig() ?? ClashConfig(); final appState = AppState( mode: clashConfig.mode, - isCompatible: config.isCompatible, selectedMap: config.currentSelectedMap, version: version, ); @@ -103,7 +99,7 @@ Future vpnService() async { ), ); final appLocalizations = await AppLocalizations.load( - other.getLocaleForString(config.locale) ?? + other.getLocaleForString(config.appSetting.locale) ?? WidgetsBinding.instance.platformDispatcher.locale, ); await app?.tip(appLocalizations.startVpn); diff --git a/lib/manager/android_manager.dart b/lib/manager/android_manager.dart index a8f3b2cd..132dbad1 100644 --- a/lib/manager/android_manager.dart +++ b/lib/manager/android_manager.dart @@ -19,39 +19,23 @@ class AndroidManager extends StatefulWidget { class _AndroidContainerState extends State { @override void initState() { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); super.initState(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } Widget _excludeContainer(Widget child) { return Selector( - selector: (_, config) => config.isExclude, - builder: (_, isExclude, child) { - app?.updateExcludeFromRecents(isExclude); + selector: (_, config) => config.appSetting.hidden, + builder: (_, hidden, child) { + app?.updateExcludeFromRecents(hidden); return child!; }, child: child, ); } - Widget _systemUiOverlayContainer(Widget child) { - return AnnotatedRegion( - value: SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - statusBarIconBrightness: Theme.of(context).brightness == Brightness.dark - ? Brightness.light - : Brightness.dark, - systemNavigationBarColor: Colors.transparent, - systemNavigationBarDividerColor: Colors.transparent, - ), - child: child, - ); - } - @override Widget build(BuildContext context) { - return _systemUiOverlayContainer( - _excludeContainer(widget.child), - ); + return _excludeContainer(widget.child); } } diff --git a/lib/manager/app_state_manager.dart b/lib/manager/app_state_manager.dart index ef57933c..761b213d 100644 --- a/lib/manager/app_state_manager.dart +++ b/lib/manager/app_state_manager.dart @@ -25,7 +25,7 @@ class _AppStateManagerState extends State final group = appState.currentGroups; final hasProfile = config.profiles.isNotEmpty; return UpdateNavigationsSelector( - openLogs: config.openLogs, + openLogs: config.appSetting.openLogs, hasProxies: group.isNotEmpty && hasProfile, ); }, diff --git a/lib/manager/clash_manager.dart b/lib/manager/clash_manager.dart index a4060e98..714c6345 100644 --- a/lib/manager/clash_manager.dart +++ b/lib/manager/clash_manager.dart @@ -64,13 +64,13 @@ class _ClashContainerState extends State with AppMessageListener { Widget _updateCoreState(Widget child) { return Selector2( selector: (_, config, clashConfig) => CoreState( - accessControl: config.isAccessControl ? config.accessControl : null, enable: config.vpnProps.enable, + accessControl: config.isAccessControl ? config.accessControl : null, ipv6: config.vpnProps.ipv6, allowBypass: config.vpnProps.allowBypass, + bypassDomain: config.vpnProps.bypassDomain, systemProxy: config.vpnProps.systemProxy, - mixedPort: clashConfig.mixedPort, - onlyProxy: config.onlyProxy, + onlyProxy: config.appSetting.onlyProxy, currentProfileName: config.currentProfile?.label ?? config.currentProfileId ?? "", ), @@ -84,10 +84,6 @@ class _ClashContainerState extends State with AppMessageListener { _changeProfile() async { WidgetsBinding.instance.addPostFrameCallback((_) async { - if (globalState.autoRun) { - globalState.autoRun = false; - return; - } final appController = globalState.appController; appController.appState.delayMap = {}; await appController.applyProfile(); @@ -136,7 +132,7 @@ class _ClashContainerState extends State with AppMessageListener { updateDelayDebounce ??= debounce(() async { await appController.updateGroupDebounce(); await appController.addCheckIpNumDebounce(); - }); + }, milliseconds: 5000); updateDelayDebounce!(); } @@ -146,7 +142,7 @@ class _ClashContainerState extends State with AppMessageListener { if (log.logLevel == LogLevel.error) { globalState.appController.showSnackBar(log.payload ?? ''); } - debugPrint("$log"); + // debugPrint("$log"); super.onLog(log); } diff --git a/lib/manager/hotkey_manager.dart b/lib/manager/hotkey_manager.dart index 4c706134..51068893 100644 --- a/lib/manager/hotkey_manager.dart +++ b/lib/manager/hotkey_manager.dart @@ -1,3 +1,4 @@ +import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/common.dart'; import 'package:fl_clash/models/config.dart'; @@ -63,7 +64,7 @@ class HotKeyManager extends StatelessWidget { return Selector>( selector: (_, config) => config.hotKeyActions, shouldRebuild: (prev, next) { - return !hotKeyActionsEquality.equals(prev, next); + return !hotKeyActionListEquality.equals(prev, next); }, builder: (_, hotKeyActions, __) { _updateHotKeys(hotKeyActions: hotKeyActions); diff --git a/lib/manager/media_manager.dart b/lib/manager/media_manager.dart index 3f373b4d..aad9d461 100644 --- a/lib/manager/media_manager.dart +++ b/lib/manager/media_manager.dart @@ -1,8 +1,6 @@ import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/models/config.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class MediaManager extends StatelessWidget { final Widget child; @@ -14,28 +12,7 @@ class MediaManager extends StatelessWidget { @override Widget build(BuildContext context) { - return Selector( - selector: (_, config) => config.scaleProps, - builder: (_, props, child) { - globalState.measure = Measure.of(context); - return child!; - // final textScaleFactor = - // WidgetsBinding.instance.platformDispatcher.textScaleFactor; - // return MediaQuery( - // data: MediaQuery.of(context).copyWith( - // textScaler: props.custom - // ? TextScaler.linear(props.scale * textScaleFactor) - // : null, - // ), - // child: Builder( - // builder: (context) { - // globalState.measure = Measure.of(context); - // return child!; - // }, - // ), - // ); - }, - child: child, - ); + globalState.measure = Measure.of(context); + return child; } } diff --git a/lib/manager/tray_manager.dart b/lib/manager/tray_manager.dart index d6902d5b..52062681 100755 --- a/lib/manager/tray_manager.dart +++ b/lib/manager/tray_manager.dart @@ -115,7 +115,15 @@ class _TrayContainerState extends State with TrayListener { }, checked: trayState.autoLaunch, ); + final adminAutoStartMenuItem = MenuItem.checkbox( + label: appLocalizations.adminAutoLaunch, + onClick: (_) async { + globalState.appController.updateAdminAutoLaunch(); + }, + checked: trayState.adminAutoLaunch, + ); menuItems.add(autoStartMenuItem); + menuItems.add(adminAutoStartMenuItem); menuItems.add(MenuItem.separator()); final exitMenuItem = MenuItem( label: appLocalizations.exit, @@ -142,9 +150,10 @@ class _TrayContainerState extends State with TrayListener { selector: (_, appState, appFlowingState, config, clashConfig) => TrayState( mode: clashConfig.mode, - autoLaunch: config.autoLaunch, + adminAutoLaunch: config.appSetting.adminAutoLaunch, + autoLaunch: config.appSetting.autoLaunch, isStart: appFlowingState.isStart, - locale: config.locale, + locale: config.appSetting.locale, systemProxy: config.desktopProps.systemProxy, tunEnable: clashConfig.tun.enable, brightness: appState.brightness, diff --git a/lib/manager/vpn_manager.dart b/lib/manager/vpn_manager.dart index 0eef88c3..2dd1e7b2 100644 --- a/lib/manager/vpn_manager.dart +++ b/lib/manager/vpn_manager.dart @@ -1,5 +1,5 @@ import 'package:fl_clash/common/app_localizations.dart'; -import 'package:fl_clash/models/config.dart'; +import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -38,13 +38,14 @@ class _VpnContainerState extends State { @override Widget build(BuildContext context) { - return Selector( - selector: (_, config) => VPNState( + return Selector2( + selector: (_, config, clashConfig) => VPNState( accessControl: config.accessControl, vpnProps: config.vpnProps, + stack: clashConfig.tun.stack, ), - shouldRebuild: (prev,next){ - if(prev != next){ + shouldRebuild: (prev, next) { + if (prev != next) { showTip(); } return prev != next; diff --git a/lib/manager/window_manager.dart b/lib/manager/window_manager.dart index 15e563f0..fdd7c084 100644 --- a/lib/manager/window_manager.dart +++ b/lib/manager/window_manager.dart @@ -23,9 +23,11 @@ class _WindowContainerState extends State with WindowListener { Function? updateLaunchDebounce; _autoLaunchContainer(Widget child) { - return Selector2( - selector: (_, config, clashConfig) => AutoLaunchState( - isAutoLaunch: config.autoLaunch, isOpenTun: clashConfig.tun.enable), + return Selector( + selector: (_, config) => AutoLaunchState( + isAutoLaunch: config.appSetting.autoLaunch, + isAdminAutoLaunch: config.appSetting.adminAutoLaunch, + ), builder: (_, state, child) { updateLaunchDebounce ??= debounce((AutoLaunchState state) { autoLaunch?.updateStatus(state); diff --git a/lib/models/app.dart b/lib/models/app.dart index f811f66e..5332f236 100644 --- a/lib/models/app.dart +++ b/lib/models/app.dart @@ -1,6 +1,3 @@ -import 'dart:io'; - -import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:flutter/material.dart'; @@ -20,7 +17,6 @@ class AppState with ChangeNotifier { Mode _mode; DelayMap _delayMap; SelectedMap _selectedMap; - bool _isCompatible; List _groups; double _viewWidth; List _requests; @@ -32,7 +28,6 @@ class AppState with ChangeNotifier { AppState({ required Mode mode, - required bool isCompatible, required SelectedMap selectedMap, required int version, }) : _navigationItems = [], @@ -49,7 +44,6 @@ class AppState with ChangeNotifier { _groups = [], _providers = [], _packages = [], - _isCompatible = isCompatible, _systemColorSchemes = const SystemColorSchemes(), _version = version; @@ -65,7 +59,7 @@ class AppState with ChangeNotifier { List get navigationItems => _navigationItems; set navigationItems(List value) { - if (!const ListEquality().equals(_navigationItems, value)) { + if (!navigationItemListEquality.equals(_navigationItems, value)) { _navigationItems = value; notifyListeners(); } @@ -168,7 +162,7 @@ class AppState with ChangeNotifier { List get groups => _groups; set groups(List value) { - if (!const ListEquality().equals(_groups, value)) { + if (!groupListEquality.equals(_groups, value)) { _groups = value; notifyListeners(); } @@ -201,23 +195,12 @@ class AppState with ChangeNotifier { } } - bool get isCompatible { - return _isCompatible; - } - - set isCompatible(bool value) { - if (_isCompatible != value) { - _isCompatible = value; - notifyListeners(); - } - } - SelectedMap get selectedMap { return _selectedMap; } set selectedMap(SelectedMap value) { - if (!const MapEquality().equals(_selectedMap, value)) { + if (!stringAndStringMapEquality.equals(_selectedMap, value)) { _selectedMap = value; notifyListeners(); } @@ -255,7 +238,7 @@ class AppState with ChangeNotifier { } set delayMap(DelayMap value) { - if (!const MapEquality().equals(_delayMap, value)) { + if (!stringAndIntQMapEquality.equals(_delayMap, value)) { _delayMap = value; notifyListeners(); } @@ -271,7 +254,7 @@ class AppState with ChangeNotifier { List get packages => _packages; set packages(List value) { - if (!const ListEquality().equals(_packages, value)) { + if (!packageListEquality.equals(_packages, value)) { _packages = value; notifyListeners(); } @@ -280,7 +263,7 @@ class AppState with ChangeNotifier { List get providers => _providers; set providers(List value) { - if (!const ListEquality().equals(_providers, value)) { + if (!externalProviderListEquality.equals(_providers, value)) { _providers = value; notifyListeners(); } diff --git a/lib/models/clash_config.dart b/lib/models/clash_config.dart index e0b1d364..51e5b300 100644 --- a/lib/models/clash_config.dart +++ b/lib/models/clash_config.dart @@ -1,8 +1,5 @@ // ignore_for_file: invalid_annotation_target -import 'dart:io'; - -import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; @@ -14,6 +11,8 @@ part 'generated/clash_config.g.dart'; part 'generated/clash_config.freezed.dart'; +const defaultTun = Tun(); + @freezed class Tun with _$Tun { const factory Tun({ @@ -24,6 +23,17 @@ class Tun with _$Tun { }) = _Tun; factory Tun.fromJson(Map json) => _$TunFromJson(json); + + factory Tun.realFromJson(Map? json) { + if (json == null) { + return defaultTun; + } + try { + return Tun.fromJson(json); + } catch (_) { + return defaultTun; + } + } } @freezed @@ -45,6 +55,8 @@ class FallbackFilter with _$FallbackFilter { _$FallbackFilterFromJson(json); } +const defaultDns = Dns(); + @freezed class Dns with _$Dns { const factory Dns({ @@ -147,7 +159,7 @@ class ClashConfig extends ChangeNotifier { _geodataLoader = geodataLoaderMemconservative, _externalController = '', _keepAliveInterval = defaultKeepAliveInterval, - _dns = const Dns(), + _dns = defaultDns, _geoXUrl = defaultGeoXMap, _rules = [], _hosts = {}; @@ -263,9 +275,6 @@ class ClashConfig extends ChangeNotifier { } Tun get tun { - if (Platform.isAndroid) { - return _tun.copyWith(enable: false); - } return _tun; } @@ -318,7 +327,7 @@ class ClashConfig extends ChangeNotifier { GeoXMap get geoXUrl => _geoXUrl; set geoXUrl(GeoXMap value) { - if (!const MapEquality().equals(value, _geoXUrl)) { + if (!stringAndStringMapEquality.equals(value, _geoXUrl)) { _geoXUrl = value; notifyListeners(); } @@ -328,7 +337,7 @@ class ClashConfig extends ChangeNotifier { HostsMap get hosts => _hosts; set hosts(HostsMap value) { - if (!const MapEquality().equals(value, _hosts)) { + if (!stringAndStringMapEquality.equals(value, _hosts)) { _hosts = value; notifyListeners(); } diff --git a/lib/models/common.dart b/lib/models/common.dart index dbb48dbf..d77c33c3 100644 --- a/lib/models/common.dart +++ b/lib/models/common.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:collection/collection.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:flutter/material.dart'; @@ -432,6 +431,14 @@ class HotKeyAction with _$HotKeyAction { _$HotKeyActionFromJson(json); } -const keyboardModifiersEquality = SetEquality(); -const hotKeyActionsEquality = ListEquality(); +typedef Validator = String? Function(String? value); + +@freezed +class Field with _$Field { + const factory Field({ + required String label, + required String value, + Validator? validator, + }) = _Field; +} \ No newline at end of file diff --git a/lib/models/config.dart b/lib/models/config.dart index c95b5169..85c2e901 100644 --- a/lib/models/config.dart +++ b/lib/models/config.dart @@ -1,17 +1,49 @@ import 'dart:io'; - -import 'package:collection/collection.dart'; +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/enum/enum.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import '../enum/enum.dart'; -import '../common/common.dart'; import 'models.dart'; part 'generated/config.g.dart'; part 'generated/config.freezed.dart'; +const defaultAppSetting = AppSetting(); + +@freezed +class AppSetting with _$AppSetting { + const factory AppSetting({ + String? locale, + @Default(false) bool onlyProxy, + @Default(false) bool autoLaunch, + @Default(false) bool adminAutoLaunch, + @Default(false) bool silentLaunch, + @Default(false) bool autoRun, + @Default(false) bool openLogs, + @Default(true) bool closeConnections, + @Default(defaultTestUrl) String testUrl, + @Default(true) bool isAnimateToPage, + @Default(true) bool autoCheckUpdate, + @Default(false) bool showLabel, + @Default(false) bool disclaimerAccepted, + @Default(true) bool minimizeOnExit, + @Default(false) bool hidden, + }) = _AppSetting; + + factory AppSetting.fromJson(Map json) => + _$AppSettingFromJson(json); + + factory AppSetting.realFromJson(Map? json) { + final appSetting = + json == null ? defaultAppSetting : AppSetting.fromJson(json); + return appSetting.copyWith( + isAnimateToPage: system.isDesktop ? false : true, + ); + } +} + @freezed class AccessControl with _$AccessControl { const factory AccessControl({ @@ -33,34 +65,6 @@ extension AccessControlExt on AccessControl { }; } -@freezed -class CoreState with _$CoreState { - const factory CoreState({ - AccessControl? accessControl, - required String currentProfileName, - required bool enable, - required bool allowBypass, - required bool systemProxy, - required int mixedPort, - required bool ipv6, - required bool onlyProxy, - }) = _CoreState; - - factory CoreState.fromJson(Map json) => - _$CoreStateFromJson(json); -} - -@freezed -class VPNState with _$VPNState { - const factory VPNState({ - required AccessControl? accessControl, - required VpnProps vpnProps, - }) = _VPNState; - - factory VPNState.fromJson(Map json) => - _$VPNStateFromJson(json); -} - @freezed class WindowProps with _$WindowProps { const factory WindowProps({ @@ -74,6 +78,28 @@ class WindowProps with _$WindowProps { json == null ? const WindowProps() : _$WindowPropsFromJson(json); } +const defaultBypassDomain = [ + "*zhihu.com", + "*zhimg.com", + "*jd.com", + "100ime-iat-api.xfyun.cn", + "*360buyimg.com", + "localhost", + "*.local", + "127.*", + "10.*", + "172.16.*", + "172.17.*", + "172.18.*", + "172.19.*", + "172.2*", + "172.30.*", + "172.31.*", + "192.168.*" +]; + +const defaultVpnProps = VpnProps(); + @freezed class VpnProps with _$VpnProps { const factory VpnProps({ @@ -81,6 +107,7 @@ class VpnProps with _$VpnProps { @Default(true) bool systemProxy, @Default(false) bool ipv6, @Default(true) bool allowBypass, + @Default(defaultBypassDomain) List bypassDomain, }) = _VpnProps; factory VpnProps.fromJson(Map? json) => @@ -97,88 +124,67 @@ class DesktopProps with _$DesktopProps { json == null ? const DesktopProps() : _$DesktopPropsFromJson(json); } -const defaultCustomFontSizeScale = 1.0; - -const defaultScaleProps = ScaleProps(); +const defaultProxiesStyle = ProxiesStyle(); @freezed -class ScaleProps with _$ScaleProps { - const factory ScaleProps({ - @Default(false) bool custom, - @Default(defaultCustomFontSizeScale) double scale, - }) = _ScaleProps; - - factory ScaleProps.fromJson(Map? json) => - json == null ? defaultScaleProps : _$ScalePropsFromJson(json); +class ProxiesStyle with _$ProxiesStyle { + const factory ProxiesStyle({ + @Default(ProxiesType.tab) ProxiesType type, + @Default(ProxiesSortType.none) ProxiesSortType sortType, + @Default(ProxiesLayout.standard) ProxiesLayout layout, + @Default(ProxiesIconStyle.standard) ProxiesIconStyle iconStyle, + @Default(ProxyCardType.expand) ProxyCardType cardType, + @Default({}) Map iconMap, + }) = _ProxiesStyle; + + factory ProxiesStyle.fromJson(Map? json) => + json == null ? defaultProxiesStyle : _$ProxiesStyleFromJson(json); } +const defaultCustomFontSizeScale = 1.0; + @JsonSerializable() class Config extends ChangeNotifier { + AppSetting _appSetting; List _profiles; - bool _isCompatible; String? _currentProfileId; - bool _autoLaunch; - bool _silentLaunch; - bool _autoRun; - bool _openLog; ThemeMode _themeMode; - String? _locale; int? _primaryColor; - ProxiesSortType _proxiesSortType; - bool _isMinimizeOnExit; bool _isAccessControl; AccessControl _accessControl; - bool _isAnimateToPage; - bool _autoCheckUpdate; - bool _isExclude; DAV? _dav; - bool _isCloseConnections; - ProxiesType _proxiesType; - ProxyCardType _proxyCardType; - ProxiesLayout _proxiesLayout; - String _testUrl; WindowProps _windowProps; - bool _onlyProxy; bool _prueBlack; VpnProps _vpnProps; - ScaleProps _scaleProps; DesktopProps _desktopProps; - bool _showLabel; bool _overrideDns; List _hotKeyActions; - bool _isDisclaimerAccepted; + ProxiesStyle _proxiesStyle; Config() : _profiles = [], - _autoLaunch = false, - _silentLaunch = false, - _autoRun = false, - _isCloseConnections = false, _themeMode = ThemeMode.system, - _openLog = false, - _isCompatible = true, _primaryColor = defaultPrimaryColor.value, - _proxiesSortType = ProxiesSortType.none, - _isMinimizeOnExit = true, _isAccessControl = false, - _autoCheckUpdate = true, - _testUrl = defaultTestUrl, _accessControl = const AccessControl(), - _isAnimateToPage = true, - _isExclude = false, - _proxyCardType = ProxyCardType.expand, _windowProps = const WindowProps(), - _proxiesType = ProxiesType.tab, _prueBlack = false, - _onlyProxy = false, - _proxiesLayout = ProxiesLayout.standard, - _vpnProps = const VpnProps(), + _vpnProps = defaultVpnProps, _desktopProps = const DesktopProps(), - _showLabel = false, _overrideDns = false, - _scaleProps = const ScaleProps(), - _isDisclaimerAccepted = false, - _hotKeyActions = []; + _appSetting = defaultAppSetting, + _hotKeyActions = [], + _proxiesStyle = defaultProxiesStyle; + + @JsonKey(fromJson: AppSetting.realFromJson) + AppSetting get appSetting => _appSetting; + + set appSetting(AppSetting value) { + if (_appSetting != value) { + _appSetting = value; + notifyListeners(); + } + } deleteProfileById(String id) { _profiles = profiles.where((element) => element.id != id).toList(); @@ -258,7 +264,7 @@ class Config extends ChangeNotifier { Set get currentUnfoldSet => currentProfile?.unfoldSet ?? {}; updateCurrentUnfoldSet(Set value) { - if (!const SetEquality().equals(currentUnfoldSet, value)) { + if (!stringSetEquality.equals(currentUnfoldSet, value)) { _setProfile( currentProfile!.copyWith( unfoldSet: value, @@ -299,39 +305,6 @@ class Config extends ChangeNotifier { } } - @JsonKey(defaultValue: false) - bool get autoLaunch { - if (!system.isDesktop) return false; - return _autoLaunch; - } - - set autoLaunch(bool value) { - if (_autoLaunch != value) { - _autoLaunch = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: false) - bool get silentLaunch => _silentLaunch; - - set silentLaunch(bool value) { - if (_silentLaunch != value) { - _silentLaunch = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: false) - bool get autoRun => _autoRun; - - set autoRun(bool value) { - if (_autoRun != value) { - _autoRun = value; - notifyListeners(); - } - } - @JsonKey(defaultValue: ThemeMode.system) ThemeMode get themeMode => _themeMode; @@ -342,25 +315,6 @@ class Config extends ChangeNotifier { } } - @JsonKey(defaultValue: false) - bool get openLogs => _openLog; - - set openLogs(bool value) { - if (_openLog != value) { - _openLog = value; - notifyListeners(); - } - } - - String? get locale => _locale; - - set locale(String? value) { - if (_locale != value) { - _locale = value; - notifyListeners(); - } - } - int? get primaryColor => _primaryColor; set primaryColor(int? value) { @@ -370,36 +324,6 @@ class Config extends ChangeNotifier { } } - @JsonKey(defaultValue: ProxiesSortType.none) - ProxiesSortType get proxiesSortType => _proxiesSortType; - - set proxiesSortType(ProxiesSortType value) { - if (_proxiesSortType != value) { - _proxiesSortType = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: ProxiesLayout.standard) - ProxiesLayout get proxiesLayout => _proxiesLayout; - - set proxiesLayout(ProxiesLayout value) { - if (_proxiesLayout != value) { - _proxiesLayout = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: true) - bool get isMinimizeOnExit => _isMinimizeOnExit; - - set isMinimizeOnExit(bool value) { - if (_isMinimizeOnExit != value) { - _isMinimizeOnExit = value; - notifyListeners(); - } - } - @JsonKey(defaultValue: false) bool get isAccessControl { if (!Platform.isAndroid) return false; @@ -431,55 +355,6 @@ class Config extends ChangeNotifier { } } - @JsonKey(defaultValue: true) - bool get isAnimateToPage { - if (!Platform.isAndroid) return false; - return _isAnimateToPage; - } - - set isAnimateToPage(bool value) { - if (_isAnimateToPage != value) { - _isAnimateToPage = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: true) - bool get isCompatible { - return _isCompatible; - } - - set isCompatible(bool value) { - if (_isCompatible != value) { - _isCompatible = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: true) - bool get autoCheckUpdate { - return _autoCheckUpdate; - } - - set autoCheckUpdate(bool value) { - if (_autoCheckUpdate != value) { - _autoCheckUpdate = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: false) - bool get onlyProxy { - return _onlyProxy; - } - - set onlyProxy(bool value) { - if (_onlyProxy != value) { - _onlyProxy = value; - notifyListeners(); - } - } - @JsonKey(defaultValue: false) bool get prueBlack { return _prueBlack; @@ -492,61 +367,6 @@ class Config extends ChangeNotifier { } } - @JsonKey(defaultValue: false) - bool get isCloseConnections { - return _isCloseConnections; - } - - set isCloseConnections(bool value) { - if (_isCloseConnections != value) { - _isCloseConnections = value; - notifyListeners(); - } - } - - @JsonKey( - defaultValue: ProxiesType.tab, - unknownEnumValue: ProxiesType.tab, - ) - ProxiesType get proxiesType => _proxiesType; - - set proxiesType(ProxiesType value) { - if (_proxiesType != value) { - _proxiesType = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: ProxyCardType.expand) - ProxyCardType get proxyCardType => _proxyCardType; - - set proxyCardType(ProxyCardType value) { - if (_proxyCardType != value) { - _proxyCardType = value; - notifyListeners(); - } - } - - @JsonKey(name: "test-url", defaultValue: defaultTestUrl) - String get testUrl => _testUrl; - - set testUrl(String value) { - if (_testUrl != value) { - _testUrl = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: false) - bool get isExclude => _isExclude; - - set isExclude(bool value) { - if (_isExclude != value) { - _isExclude = value; - notifyListeners(); - } - } - WindowProps get windowProps => _windowProps; set windowProps(WindowProps value) { @@ -574,25 +394,6 @@ class Config extends ChangeNotifier { } } - ScaleProps get scaleProps => _scaleProps; - - set scaleProps(ScaleProps value) { - if (_scaleProps != value) { - _scaleProps = value; - notifyListeners(); - } - } - - @JsonKey(defaultValue: false) - bool get showLabel => _showLabel; - - set showLabel(bool value) { - if (_showLabel != value) { - _showLabel = value; - notifyListeners(); - } - } - @JsonKey(defaultValue: false) bool get overrideDns => _overrideDns; @@ -603,16 +404,6 @@ class Config extends ChangeNotifier { } } - @JsonKey(defaultValue: false) - bool get isDisclaimerAccepted => _isDisclaimerAccepted; - - set isDisclaimerAccepted(bool value) { - if (_isDisclaimerAccepted != value) { - _isDisclaimerAccepted = value; - notifyListeners(); - } - } - @JsonKey(defaultValue: []) List get hotKeyActions => _hotKeyActions; @@ -623,6 +414,19 @@ class Config extends ChangeNotifier { } } + ProxiesStyle get proxiesStyle => _proxiesStyle; + + set proxiesStyle(ProxiesStyle value) { + if (_proxiesStyle != value || + !stringAndStringMapEntryIterableEquality.equals( + _proxiesStyle.iconMap.entries, + value.iconMap.entries, + )) { + _proxiesStyle = value; + notifyListeners(); + } + } + updateOrAddHotKeyAction(HotKeyAction hotKeyAction) { final index = _hotKeyActions.indexWhere((item) => item.action == hotKeyAction.action); @@ -648,28 +452,16 @@ class Config extends ChangeNotifier { _currentProfileId = _profiles.first.id; } if (onlyProfiles) return; + _appSetting = config._appSetting; _currentProfileId = config._currentProfileId; - _isCloseConnections = config._isCloseConnections; - _isCompatible = config._isCompatible; - _autoLaunch = config._autoLaunch; _dav = config._dav; - _silentLaunch = config._silentLaunch; - _autoRun = config._autoRun; - _proxiesType = config._proxiesType; - _openLog = config._openLog; _themeMode = config._themeMode; - _locale = config._locale; _primaryColor = config._primaryColor; - _proxiesSortType = config._proxiesSortType; - _isMinimizeOnExit = config._isMinimizeOnExit; _isAccessControl = config._isAccessControl; _accessControl = config._accessControl; - _isAnimateToPage = config._isAnimateToPage; - _autoCheckUpdate = config._autoCheckUpdate; _prueBlack = config._prueBlack; - _testUrl = config._testUrl; - _isExclude = config._isExclude; _windowProps = config._windowProps; + _proxiesStyle = config._proxiesStyle; _vpnProps = config._vpnProps; _overrideDns = config._overrideDns; _desktopProps = config._desktopProps; diff --git a/lib/models/ffi.dart b/lib/models/ffi.dart index 50b39e4d..ae347d6a 100644 --- a/lib/models/ffi.dart +++ b/lib/models/ffi.dart @@ -8,6 +8,41 @@ part 'generated/ffi.g.dart'; part 'generated/ffi.freezed.dart'; +@freezed +class CoreState with _$CoreState { + const factory CoreState({ + required bool enable, + AccessControl? accessControl, + required String currentProfileName, + required bool allowBypass, + required bool systemProxy, + required List bypassDomain, + required bool ipv6, + required bool onlyProxy, + }) = _CoreState; + + factory CoreState.fromJson(Map json) => + _$CoreStateFromJson(json); +} + +@freezed +class AndroidVpnOptions with _$AndroidVpnOptions { + const factory AndroidVpnOptions({ + required bool enable, + required int port, + required AccessControl? accessControl, + required bool allowBypass, + required bool systemProxy, + required List bypassDomain, + required String ipv4Address, + required String ipv6Address, + required String dnsServerAddress, + }) = _AndroidVpnOptions; + + factory AndroidVpnOptions.fromJson(Map json) => + _$AndroidVpnOptionsFromJson(json); +} + @freezed class ConfigExtendedParams with _$ConfigExtendedParams { const factory ConfigExtendedParams({ diff --git a/lib/models/generated/common.freezed.dart b/lib/models/generated/common.freezed.dart index 60769bae..add0473f 100644 --- a/lib/models/generated/common.freezed.dart +++ b/lib/models/generated/common.freezed.dart @@ -2468,3 +2468,153 @@ abstract class _HotKeyAction implements HotKeyAction { _$$HotKeyActionImplCopyWith<_$HotKeyActionImpl> get copyWith => throw _privateConstructorUsedError; } + +/// @nodoc +mixin _$Field { + String get label => throw _privateConstructorUsedError; + String get value => throw _privateConstructorUsedError; + Validator? get validator => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $FieldCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $FieldCopyWith<$Res> { + factory $FieldCopyWith(Field value, $Res Function(Field) then) = + _$FieldCopyWithImpl<$Res, Field>; + @useResult + $Res call({String label, String value, Validator? validator}); +} + +/// @nodoc +class _$FieldCopyWithImpl<$Res, $Val extends Field> + implements $FieldCopyWith<$Res> { + _$FieldCopyWithImpl(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? label = null, + Object? value = null, + Object? validator = freezed, + }) { + return _then(_value.copyWith( + label: null == label + ? _value.label + : label // ignore: cast_nullable_to_non_nullable + as String, + value: null == value + ? _value.value + : value // ignore: cast_nullable_to_non_nullable + as String, + validator: freezed == validator + ? _value.validator + : validator // ignore: cast_nullable_to_non_nullable + as Validator?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$FieldImplCopyWith<$Res> implements $FieldCopyWith<$Res> { + factory _$$FieldImplCopyWith( + _$FieldImpl value, $Res Function(_$FieldImpl) then) = + __$$FieldImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String label, String value, Validator? validator}); +} + +/// @nodoc +class __$$FieldImplCopyWithImpl<$Res> + extends _$FieldCopyWithImpl<$Res, _$FieldImpl> + implements _$$FieldImplCopyWith<$Res> { + __$$FieldImplCopyWithImpl( + _$FieldImpl _value, $Res Function(_$FieldImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? label = null, + Object? value = null, + Object? validator = freezed, + }) { + return _then(_$FieldImpl( + label: null == label + ? _value.label + : label // ignore: cast_nullable_to_non_nullable + as String, + value: null == value + ? _value.value + : value // ignore: cast_nullable_to_non_nullable + as String, + validator: freezed == validator + ? _value.validator + : validator // ignore: cast_nullable_to_non_nullable + as Validator?, + )); + } +} + +/// @nodoc + +class _$FieldImpl implements _Field { + const _$FieldImpl({required this.label, required this.value, this.validator}); + + @override + final String label; + @override + final String value; + @override + final Validator? validator; + + @override + String toString() { + return 'Field(label: $label, value: $value, validator: $validator)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$FieldImpl && + (identical(other.label, label) || other.label == label) && + (identical(other.value, value) || other.value == value) && + (identical(other.validator, validator) || + other.validator == validator)); + } + + @override + int get hashCode => Object.hash(runtimeType, label, value, validator); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$FieldImplCopyWith<_$FieldImpl> get copyWith => + __$$FieldImplCopyWithImpl<_$FieldImpl>(this, _$identity); +} + +abstract class _Field implements Field { + const factory _Field( + {required final String label, + required final String value, + final Validator? validator}) = _$FieldImpl; + + @override + String get label; + @override + String get value; + @override + Validator? get validator; + @override + @JsonKey(ignore: true) + _$$FieldImplCopyWith<_$FieldImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/generated/config.freezed.dart b/lib/models/generated/config.freezed.dart index c4358c5e..80ea9537 100644 --- a/lib/models/generated/config.freezed.dart +++ b/lib/models/generated/config.freezed.dart @@ -14,6 +14,467 @@ T _$identity(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'); +AppSetting _$AppSettingFromJson(Map json) { + return _AppSetting.fromJson(json); +} + +/// @nodoc +mixin _$AppSetting { + String? get locale => throw _privateConstructorUsedError; + bool get onlyProxy => throw _privateConstructorUsedError; + bool get autoLaunch => throw _privateConstructorUsedError; + bool get adminAutoLaunch => throw _privateConstructorUsedError; + bool get silentLaunch => throw _privateConstructorUsedError; + bool get autoRun => throw _privateConstructorUsedError; + bool get openLogs => throw _privateConstructorUsedError; + bool get closeConnections => throw _privateConstructorUsedError; + String get testUrl => throw _privateConstructorUsedError; + bool get isAnimateToPage => throw _privateConstructorUsedError; + bool get autoCheckUpdate => throw _privateConstructorUsedError; + bool get showLabel => throw _privateConstructorUsedError; + bool get disclaimerAccepted => throw _privateConstructorUsedError; + bool get minimizeOnExit => throw _privateConstructorUsedError; + bool get hidden => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $AppSettingCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AppSettingCopyWith<$Res> { + factory $AppSettingCopyWith( + AppSetting value, $Res Function(AppSetting) then) = + _$AppSettingCopyWithImpl<$Res, AppSetting>; + @useResult + $Res call( + {String? locale, + bool onlyProxy, + bool autoLaunch, + bool adminAutoLaunch, + bool silentLaunch, + bool autoRun, + bool openLogs, + bool closeConnections, + String testUrl, + bool isAnimateToPage, + bool autoCheckUpdate, + bool showLabel, + bool disclaimerAccepted, + bool minimizeOnExit, + bool hidden}); +} + +/// @nodoc +class _$AppSettingCopyWithImpl<$Res, $Val extends AppSetting> + implements $AppSettingCopyWith<$Res> { + _$AppSettingCopyWithImpl(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? locale = freezed, + Object? onlyProxy = null, + Object? autoLaunch = null, + Object? adminAutoLaunch = null, + Object? silentLaunch = null, + Object? autoRun = null, + Object? openLogs = null, + Object? closeConnections = null, + Object? testUrl = null, + Object? isAnimateToPage = null, + Object? autoCheckUpdate = null, + Object? showLabel = null, + Object? disclaimerAccepted = null, + Object? minimizeOnExit = null, + Object? hidden = null, + }) { + return _then(_value.copyWith( + locale: freezed == locale + ? _value.locale + : locale // ignore: cast_nullable_to_non_nullable + as String?, + onlyProxy: null == onlyProxy + ? _value.onlyProxy + : onlyProxy // ignore: cast_nullable_to_non_nullable + as bool, + autoLaunch: null == autoLaunch + ? _value.autoLaunch + : autoLaunch // ignore: cast_nullable_to_non_nullable + as bool, + adminAutoLaunch: null == adminAutoLaunch + ? _value.adminAutoLaunch + : adminAutoLaunch // ignore: cast_nullable_to_non_nullable + as bool, + silentLaunch: null == silentLaunch + ? _value.silentLaunch + : silentLaunch // ignore: cast_nullable_to_non_nullable + as bool, + autoRun: null == autoRun + ? _value.autoRun + : autoRun // ignore: cast_nullable_to_non_nullable + as bool, + openLogs: null == openLogs + ? _value.openLogs + : openLogs // ignore: cast_nullable_to_non_nullable + as bool, + closeConnections: null == closeConnections + ? _value.closeConnections + : closeConnections // ignore: cast_nullable_to_non_nullable + as bool, + testUrl: null == testUrl + ? _value.testUrl + : testUrl // ignore: cast_nullable_to_non_nullable + as String, + isAnimateToPage: null == isAnimateToPage + ? _value.isAnimateToPage + : isAnimateToPage // ignore: cast_nullable_to_non_nullable + as bool, + autoCheckUpdate: null == autoCheckUpdate + ? _value.autoCheckUpdate + : autoCheckUpdate // ignore: cast_nullable_to_non_nullable + as bool, + showLabel: null == showLabel + ? _value.showLabel + : showLabel // ignore: cast_nullable_to_non_nullable + as bool, + disclaimerAccepted: null == disclaimerAccepted + ? _value.disclaimerAccepted + : disclaimerAccepted // ignore: cast_nullable_to_non_nullable + as bool, + minimizeOnExit: null == minimizeOnExit + ? _value.minimizeOnExit + : minimizeOnExit // ignore: cast_nullable_to_non_nullable + as bool, + hidden: null == hidden + ? _value.hidden + : hidden // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$AppSettingImplCopyWith<$Res> + implements $AppSettingCopyWith<$Res> { + factory _$$AppSettingImplCopyWith( + _$AppSettingImpl value, $Res Function(_$AppSettingImpl) then) = + __$$AppSettingImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? locale, + bool onlyProxy, + bool autoLaunch, + bool adminAutoLaunch, + bool silentLaunch, + bool autoRun, + bool openLogs, + bool closeConnections, + String testUrl, + bool isAnimateToPage, + bool autoCheckUpdate, + bool showLabel, + bool disclaimerAccepted, + bool minimizeOnExit, + bool hidden}); +} + +/// @nodoc +class __$$AppSettingImplCopyWithImpl<$Res> + extends _$AppSettingCopyWithImpl<$Res, _$AppSettingImpl> + implements _$$AppSettingImplCopyWith<$Res> { + __$$AppSettingImplCopyWithImpl( + _$AppSettingImpl _value, $Res Function(_$AppSettingImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? locale = freezed, + Object? onlyProxy = null, + Object? autoLaunch = null, + Object? adminAutoLaunch = null, + Object? silentLaunch = null, + Object? autoRun = null, + Object? openLogs = null, + Object? closeConnections = null, + Object? testUrl = null, + Object? isAnimateToPage = null, + Object? autoCheckUpdate = null, + Object? showLabel = null, + Object? disclaimerAccepted = null, + Object? minimizeOnExit = null, + Object? hidden = null, + }) { + return _then(_$AppSettingImpl( + locale: freezed == locale + ? _value.locale + : locale // ignore: cast_nullable_to_non_nullable + as String?, + onlyProxy: null == onlyProxy + ? _value.onlyProxy + : onlyProxy // ignore: cast_nullable_to_non_nullable + as bool, + autoLaunch: null == autoLaunch + ? _value.autoLaunch + : autoLaunch // ignore: cast_nullable_to_non_nullable + as bool, + adminAutoLaunch: null == adminAutoLaunch + ? _value.adminAutoLaunch + : adminAutoLaunch // ignore: cast_nullable_to_non_nullable + as bool, + silentLaunch: null == silentLaunch + ? _value.silentLaunch + : silentLaunch // ignore: cast_nullable_to_non_nullable + as bool, + autoRun: null == autoRun + ? _value.autoRun + : autoRun // ignore: cast_nullable_to_non_nullable + as bool, + openLogs: null == openLogs + ? _value.openLogs + : openLogs // ignore: cast_nullable_to_non_nullable + as bool, + closeConnections: null == closeConnections + ? _value.closeConnections + : closeConnections // ignore: cast_nullable_to_non_nullable + as bool, + testUrl: null == testUrl + ? _value.testUrl + : testUrl // ignore: cast_nullable_to_non_nullable + as String, + isAnimateToPage: null == isAnimateToPage + ? _value.isAnimateToPage + : isAnimateToPage // ignore: cast_nullable_to_non_nullable + as bool, + autoCheckUpdate: null == autoCheckUpdate + ? _value.autoCheckUpdate + : autoCheckUpdate // ignore: cast_nullable_to_non_nullable + as bool, + showLabel: null == showLabel + ? _value.showLabel + : showLabel // ignore: cast_nullable_to_non_nullable + as bool, + disclaimerAccepted: null == disclaimerAccepted + ? _value.disclaimerAccepted + : disclaimerAccepted // ignore: cast_nullable_to_non_nullable + as bool, + minimizeOnExit: null == minimizeOnExit + ? _value.minimizeOnExit + : minimizeOnExit // ignore: cast_nullable_to_non_nullable + as bool, + hidden: null == hidden + ? _value.hidden + : hidden // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$AppSettingImpl implements _AppSetting { + const _$AppSettingImpl( + {this.locale, + this.onlyProxy = false, + this.autoLaunch = false, + this.adminAutoLaunch = false, + this.silentLaunch = false, + this.autoRun = false, + this.openLogs = false, + this.closeConnections = true, + this.testUrl = defaultTestUrl, + this.isAnimateToPage = true, + this.autoCheckUpdate = true, + this.showLabel = false, + this.disclaimerAccepted = false, + this.minimizeOnExit = true, + this.hidden = false}); + + factory _$AppSettingImpl.fromJson(Map json) => + _$$AppSettingImplFromJson(json); + + @override + final String? locale; + @override + @JsonKey() + final bool onlyProxy; + @override + @JsonKey() + final bool autoLaunch; + @override + @JsonKey() + final bool adminAutoLaunch; + @override + @JsonKey() + final bool silentLaunch; + @override + @JsonKey() + final bool autoRun; + @override + @JsonKey() + final bool openLogs; + @override + @JsonKey() + final bool closeConnections; + @override + @JsonKey() + final String testUrl; + @override + @JsonKey() + final bool isAnimateToPage; + @override + @JsonKey() + final bool autoCheckUpdate; + @override + @JsonKey() + final bool showLabel; + @override + @JsonKey() + final bool disclaimerAccepted; + @override + @JsonKey() + final bool minimizeOnExit; + @override + @JsonKey() + final bool hidden; + + @override + String toString() { + return 'AppSetting(locale: $locale, onlyProxy: $onlyProxy, autoLaunch: $autoLaunch, adminAutoLaunch: $adminAutoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AppSettingImpl && + (identical(other.locale, locale) || other.locale == locale) && + (identical(other.onlyProxy, onlyProxy) || + other.onlyProxy == onlyProxy) && + (identical(other.autoLaunch, autoLaunch) || + other.autoLaunch == autoLaunch) && + (identical(other.adminAutoLaunch, adminAutoLaunch) || + other.adminAutoLaunch == adminAutoLaunch) && + (identical(other.silentLaunch, silentLaunch) || + other.silentLaunch == silentLaunch) && + (identical(other.autoRun, autoRun) || other.autoRun == autoRun) && + (identical(other.openLogs, openLogs) || + other.openLogs == openLogs) && + (identical(other.closeConnections, closeConnections) || + other.closeConnections == closeConnections) && + (identical(other.testUrl, testUrl) || other.testUrl == testUrl) && + (identical(other.isAnimateToPage, isAnimateToPage) || + other.isAnimateToPage == isAnimateToPage) && + (identical(other.autoCheckUpdate, autoCheckUpdate) || + other.autoCheckUpdate == autoCheckUpdate) && + (identical(other.showLabel, showLabel) || + other.showLabel == showLabel) && + (identical(other.disclaimerAccepted, disclaimerAccepted) || + other.disclaimerAccepted == disclaimerAccepted) && + (identical(other.minimizeOnExit, minimizeOnExit) || + other.minimizeOnExit == minimizeOnExit) && + (identical(other.hidden, hidden) || other.hidden == hidden)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + locale, + onlyProxy, + autoLaunch, + adminAutoLaunch, + silentLaunch, + autoRun, + openLogs, + closeConnections, + testUrl, + isAnimateToPage, + autoCheckUpdate, + showLabel, + disclaimerAccepted, + minimizeOnExit, + hidden); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$AppSettingImplCopyWith<_$AppSettingImpl> get copyWith => + __$$AppSettingImplCopyWithImpl<_$AppSettingImpl>(this, _$identity); + + @override + Map toJson() { + return _$$AppSettingImplToJson( + this, + ); + } +} + +abstract class _AppSetting implements AppSetting { + const factory _AppSetting( + {final String? locale, + final bool onlyProxy, + final bool autoLaunch, + final bool adminAutoLaunch, + final bool silentLaunch, + final bool autoRun, + final bool openLogs, + final bool closeConnections, + final String testUrl, + final bool isAnimateToPage, + final bool autoCheckUpdate, + final bool showLabel, + final bool disclaimerAccepted, + final bool minimizeOnExit, + final bool hidden}) = _$AppSettingImpl; + + factory _AppSetting.fromJson(Map json) = + _$AppSettingImpl.fromJson; + + @override + String? get locale; + @override + bool get onlyProxy; + @override + bool get autoLaunch; + @override + bool get adminAutoLaunch; + @override + bool get silentLaunch; + @override + bool get autoRun; + @override + bool get openLogs; + @override + bool get closeConnections; + @override + String get testUrl; + @override + bool get isAnimateToPage; + @override + bool get autoCheckUpdate; + @override + bool get showLabel; + @override + bool get disclaimerAccepted; + @override + bool get minimizeOnExit; + @override + bool get hidden; + @override + @JsonKey(ignore: true) + _$$AppSettingImplCopyWith<_$AppSettingImpl> get copyWith => + throw _privateConstructorUsedError; +} + AccessControl _$AccessControlFromJson(Map json) { return _AccessControl.fromJson(json); } @@ -262,500 +723,6 @@ abstract class _AccessControl implements AccessControl { throw _privateConstructorUsedError; } -CoreState _$CoreStateFromJson(Map json) { - return _CoreState.fromJson(json); -} - -/// @nodoc -mixin _$CoreState { - AccessControl? get accessControl => throw _privateConstructorUsedError; - String get currentProfileName => throw _privateConstructorUsedError; - bool get enable => throw _privateConstructorUsedError; - bool get allowBypass => throw _privateConstructorUsedError; - bool get systemProxy => throw _privateConstructorUsedError; - int get mixedPort => throw _privateConstructorUsedError; - bool get ipv6 => throw _privateConstructorUsedError; - bool get onlyProxy => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $CoreStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $CoreStateCopyWith<$Res> { - factory $CoreStateCopyWith(CoreState value, $Res Function(CoreState) then) = - _$CoreStateCopyWithImpl<$Res, CoreState>; - @useResult - $Res call( - {AccessControl? accessControl, - String currentProfileName, - bool enable, - bool allowBypass, - bool systemProxy, - int mixedPort, - bool ipv6, - bool onlyProxy}); - - $AccessControlCopyWith<$Res>? get accessControl; -} - -/// @nodoc -class _$CoreStateCopyWithImpl<$Res, $Val extends CoreState> - implements $CoreStateCopyWith<$Res> { - _$CoreStateCopyWithImpl(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? accessControl = freezed, - Object? currentProfileName = null, - Object? enable = null, - Object? allowBypass = null, - Object? systemProxy = null, - Object? mixedPort = null, - Object? ipv6 = null, - Object? onlyProxy = null, - }) { - return _then(_value.copyWith( - accessControl: freezed == accessControl - ? _value.accessControl - : accessControl // ignore: cast_nullable_to_non_nullable - as AccessControl?, - currentProfileName: null == currentProfileName - ? _value.currentProfileName - : currentProfileName // ignore: cast_nullable_to_non_nullable - as String, - enable: null == enable - ? _value.enable - : enable // ignore: cast_nullable_to_non_nullable - as bool, - allowBypass: null == allowBypass - ? _value.allowBypass - : allowBypass // ignore: cast_nullable_to_non_nullable - as bool, - systemProxy: null == systemProxy - ? _value.systemProxy - : systemProxy // ignore: cast_nullable_to_non_nullable - as bool, - mixedPort: null == mixedPort - ? _value.mixedPort - : mixedPort // ignore: cast_nullable_to_non_nullable - as int, - ipv6: null == ipv6 - ? _value.ipv6 - : ipv6 // ignore: cast_nullable_to_non_nullable - as bool, - onlyProxy: null == onlyProxy - ? _value.onlyProxy - : onlyProxy // ignore: cast_nullable_to_non_nullable - as bool, - ) as $Val); - } - - @override - @pragma('vm:prefer-inline') - $AccessControlCopyWith<$Res>? get accessControl { - if (_value.accessControl == null) { - return null; - } - - return $AccessControlCopyWith<$Res>(_value.accessControl!, (value) { - return _then(_value.copyWith(accessControl: value) as $Val); - }); - } -} - -/// @nodoc -abstract class _$$CoreStateImplCopyWith<$Res> - implements $CoreStateCopyWith<$Res> { - factory _$$CoreStateImplCopyWith( - _$CoreStateImpl value, $Res Function(_$CoreStateImpl) then) = - __$$CoreStateImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {AccessControl? accessControl, - String currentProfileName, - bool enable, - bool allowBypass, - bool systemProxy, - int mixedPort, - bool ipv6, - bool onlyProxy}); - - @override - $AccessControlCopyWith<$Res>? get accessControl; -} - -/// @nodoc -class __$$CoreStateImplCopyWithImpl<$Res> - extends _$CoreStateCopyWithImpl<$Res, _$CoreStateImpl> - implements _$$CoreStateImplCopyWith<$Res> { - __$$CoreStateImplCopyWithImpl( - _$CoreStateImpl _value, $Res Function(_$CoreStateImpl) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? accessControl = freezed, - Object? currentProfileName = null, - Object? enable = null, - Object? allowBypass = null, - Object? systemProxy = null, - Object? mixedPort = null, - Object? ipv6 = null, - Object? onlyProxy = null, - }) { - return _then(_$CoreStateImpl( - accessControl: freezed == accessControl - ? _value.accessControl - : accessControl // ignore: cast_nullable_to_non_nullable - as AccessControl?, - currentProfileName: null == currentProfileName - ? _value.currentProfileName - : currentProfileName // ignore: cast_nullable_to_non_nullable - as String, - enable: null == enable - ? _value.enable - : enable // ignore: cast_nullable_to_non_nullable - as bool, - allowBypass: null == allowBypass - ? _value.allowBypass - : allowBypass // ignore: cast_nullable_to_non_nullable - as bool, - systemProxy: null == systemProxy - ? _value.systemProxy - : systemProxy // ignore: cast_nullable_to_non_nullable - as bool, - mixedPort: null == mixedPort - ? _value.mixedPort - : mixedPort // ignore: cast_nullable_to_non_nullable - as int, - ipv6: null == ipv6 - ? _value.ipv6 - : ipv6 // ignore: cast_nullable_to_non_nullable - as bool, - onlyProxy: null == onlyProxy - ? _value.onlyProxy - : onlyProxy // ignore: cast_nullable_to_non_nullable - as bool, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$CoreStateImpl implements _CoreState { - const _$CoreStateImpl( - {this.accessControl, - required this.currentProfileName, - required this.enable, - required this.allowBypass, - required this.systemProxy, - required this.mixedPort, - required this.ipv6, - required this.onlyProxy}); - - factory _$CoreStateImpl.fromJson(Map json) => - _$$CoreStateImplFromJson(json); - - @override - final AccessControl? accessControl; - @override - final String currentProfileName; - @override - final bool enable; - @override - final bool allowBypass; - @override - final bool systemProxy; - @override - final int mixedPort; - @override - final bool ipv6; - @override - final bool onlyProxy; - - @override - String toString() { - return 'CoreState(accessControl: $accessControl, currentProfileName: $currentProfileName, enable: $enable, allowBypass: $allowBypass, systemProxy: $systemProxy, mixedPort: $mixedPort, ipv6: $ipv6, onlyProxy: $onlyProxy)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$CoreStateImpl && - (identical(other.accessControl, accessControl) || - other.accessControl == accessControl) && - (identical(other.currentProfileName, currentProfileName) || - other.currentProfileName == currentProfileName) && - (identical(other.enable, enable) || other.enable == enable) && - (identical(other.allowBypass, allowBypass) || - other.allowBypass == allowBypass) && - (identical(other.systemProxy, systemProxy) || - other.systemProxy == systemProxy) && - (identical(other.mixedPort, mixedPort) || - other.mixedPort == mixedPort) && - (identical(other.ipv6, ipv6) || other.ipv6 == ipv6) && - (identical(other.onlyProxy, onlyProxy) || - other.onlyProxy == onlyProxy)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, - accessControl, - currentProfileName, - enable, - allowBypass, - systemProxy, - mixedPort, - ipv6, - onlyProxy); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$CoreStateImplCopyWith<_$CoreStateImpl> get copyWith => - __$$CoreStateImplCopyWithImpl<_$CoreStateImpl>(this, _$identity); - - @override - Map toJson() { - return _$$CoreStateImplToJson( - this, - ); - } -} - -abstract class _CoreState implements CoreState { - const factory _CoreState( - {final AccessControl? accessControl, - required final String currentProfileName, - required final bool enable, - required final bool allowBypass, - required final bool systemProxy, - required final int mixedPort, - required final bool ipv6, - required final bool onlyProxy}) = _$CoreStateImpl; - - factory _CoreState.fromJson(Map json) = - _$CoreStateImpl.fromJson; - - @override - AccessControl? get accessControl; - @override - String get currentProfileName; - @override - bool get enable; - @override - bool get allowBypass; - @override - bool get systemProxy; - @override - int get mixedPort; - @override - bool get ipv6; - @override - bool get onlyProxy; - @override - @JsonKey(ignore: true) - _$$CoreStateImplCopyWith<_$CoreStateImpl> get copyWith => - throw _privateConstructorUsedError; -} - -VPNState _$VPNStateFromJson(Map json) { - return _VPNState.fromJson(json); -} - -/// @nodoc -mixin _$VPNState { - AccessControl? get accessControl => throw _privateConstructorUsedError; - VpnProps get vpnProps => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $VPNStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $VPNStateCopyWith<$Res> { - factory $VPNStateCopyWith(VPNState value, $Res Function(VPNState) then) = - _$VPNStateCopyWithImpl<$Res, VPNState>; - @useResult - $Res call({AccessControl? accessControl, VpnProps vpnProps}); - - $AccessControlCopyWith<$Res>? get accessControl; - $VpnPropsCopyWith<$Res> get vpnProps; -} - -/// @nodoc -class _$VPNStateCopyWithImpl<$Res, $Val extends VPNState> - implements $VPNStateCopyWith<$Res> { - _$VPNStateCopyWithImpl(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? accessControl = freezed, - Object? vpnProps = null, - }) { - return _then(_value.copyWith( - accessControl: freezed == accessControl - ? _value.accessControl - : accessControl // ignore: cast_nullable_to_non_nullable - as AccessControl?, - vpnProps: null == vpnProps - ? _value.vpnProps - : vpnProps // ignore: cast_nullable_to_non_nullable - as VpnProps, - ) as $Val); - } - - @override - @pragma('vm:prefer-inline') - $AccessControlCopyWith<$Res>? get accessControl { - if (_value.accessControl == null) { - return null; - } - - return $AccessControlCopyWith<$Res>(_value.accessControl!, (value) { - return _then(_value.copyWith(accessControl: value) as $Val); - }); - } - - @override - @pragma('vm:prefer-inline') - $VpnPropsCopyWith<$Res> get vpnProps { - return $VpnPropsCopyWith<$Res>(_value.vpnProps, (value) { - return _then(_value.copyWith(vpnProps: value) as $Val); - }); - } -} - -/// @nodoc -abstract class _$$VPNStateImplCopyWith<$Res> - implements $VPNStateCopyWith<$Res> { - factory _$$VPNStateImplCopyWith( - _$VPNStateImpl value, $Res Function(_$VPNStateImpl) then) = - __$$VPNStateImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({AccessControl? accessControl, VpnProps vpnProps}); - - @override - $AccessControlCopyWith<$Res>? get accessControl; - @override - $VpnPropsCopyWith<$Res> get vpnProps; -} - -/// @nodoc -class __$$VPNStateImplCopyWithImpl<$Res> - extends _$VPNStateCopyWithImpl<$Res, _$VPNStateImpl> - implements _$$VPNStateImplCopyWith<$Res> { - __$$VPNStateImplCopyWithImpl( - _$VPNStateImpl _value, $Res Function(_$VPNStateImpl) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? accessControl = freezed, - Object? vpnProps = null, - }) { - return _then(_$VPNStateImpl( - accessControl: freezed == accessControl - ? _value.accessControl - : accessControl // ignore: cast_nullable_to_non_nullable - as AccessControl?, - vpnProps: null == vpnProps - ? _value.vpnProps - : vpnProps // ignore: cast_nullable_to_non_nullable - as VpnProps, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$VPNStateImpl implements _VPNState { - const _$VPNStateImpl({required this.accessControl, required this.vpnProps}); - - factory _$VPNStateImpl.fromJson(Map json) => - _$$VPNStateImplFromJson(json); - - @override - final AccessControl? accessControl; - @override - final VpnProps vpnProps; - - @override - String toString() { - return 'VPNState(accessControl: $accessControl, vpnProps: $vpnProps)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$VPNStateImpl && - (identical(other.accessControl, accessControl) || - other.accessControl == accessControl) && - (identical(other.vpnProps, vpnProps) || - other.vpnProps == vpnProps)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash(runtimeType, accessControl, vpnProps); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$VPNStateImplCopyWith<_$VPNStateImpl> get copyWith => - __$$VPNStateImplCopyWithImpl<_$VPNStateImpl>(this, _$identity); - - @override - Map toJson() { - return _$$VPNStateImplToJson( - this, - ); - } -} - -abstract class _VPNState implements VPNState { - const factory _VPNState( - {required final AccessControl? accessControl, - required final VpnProps vpnProps}) = _$VPNStateImpl; - - factory _VPNState.fromJson(Map json) = - _$VPNStateImpl.fromJson; - - @override - AccessControl? get accessControl; - @override - VpnProps get vpnProps; - @override - @JsonKey(ignore: true) - _$$VPNStateImplCopyWith<_$VPNStateImpl> get copyWith => - throw _privateConstructorUsedError; -} - WindowProps _$WindowPropsFromJson(Map json) { return _WindowProps.fromJson(json); } @@ -957,6 +924,7 @@ mixin _$VpnProps { bool get systemProxy => throw _privateConstructorUsedError; bool get ipv6 => throw _privateConstructorUsedError; bool get allowBypass => throw _privateConstructorUsedError; + List get bypassDomain => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -969,7 +937,12 @@ abstract class $VpnPropsCopyWith<$Res> { factory $VpnPropsCopyWith(VpnProps value, $Res Function(VpnProps) then) = _$VpnPropsCopyWithImpl<$Res, VpnProps>; @useResult - $Res call({bool enable, bool systemProxy, bool ipv6, bool allowBypass}); + $Res call( + {bool enable, + bool systemProxy, + bool ipv6, + bool allowBypass, + List bypassDomain}); } /// @nodoc @@ -989,6 +962,7 @@ class _$VpnPropsCopyWithImpl<$Res, $Val extends VpnProps> Object? systemProxy = null, Object? ipv6 = null, Object? allowBypass = null, + Object? bypassDomain = null, }) { return _then(_value.copyWith( enable: null == enable @@ -1007,6 +981,10 @@ class _$VpnPropsCopyWithImpl<$Res, $Val extends VpnProps> ? _value.allowBypass : allowBypass // ignore: cast_nullable_to_non_nullable as bool, + bypassDomain: null == bypassDomain + ? _value.bypassDomain + : bypassDomain // ignore: cast_nullable_to_non_nullable + as List, ) as $Val); } } @@ -1019,7 +997,12 @@ abstract class _$$VpnPropsImplCopyWith<$Res> __$$VpnPropsImplCopyWithImpl<$Res>; @override @useResult - $Res call({bool enable, bool systemProxy, bool ipv6, bool allowBypass}); + $Res call( + {bool enable, + bool systemProxy, + bool ipv6, + bool allowBypass, + List bypassDomain}); } /// @nodoc @@ -1037,6 +1020,7 @@ class __$$VpnPropsImplCopyWithImpl<$Res> Object? systemProxy = null, Object? ipv6 = null, Object? allowBypass = null, + Object? bypassDomain = null, }) { return _then(_$VpnPropsImpl( enable: null == enable @@ -1055,6 +1039,10 @@ class __$$VpnPropsImplCopyWithImpl<$Res> ? _value.allowBypass : allowBypass // ignore: cast_nullable_to_non_nullable as bool, + bypassDomain: null == bypassDomain + ? _value._bypassDomain + : bypassDomain // ignore: cast_nullable_to_non_nullable + as List, )); } } @@ -1066,7 +1054,9 @@ class _$VpnPropsImpl implements _VpnProps { {this.enable = true, this.systemProxy = true, this.ipv6 = false, - this.allowBypass = true}); + this.allowBypass = true, + final List bypassDomain = defaultBypassDomain}) + : _bypassDomain = bypassDomain; factory _$VpnPropsImpl.fromJson(Map json) => _$$VpnPropsImplFromJson(json); @@ -1083,10 +1073,18 @@ class _$VpnPropsImpl implements _VpnProps { @override @JsonKey() final bool allowBypass; + final List _bypassDomain; + @override + @JsonKey() + List get bypassDomain { + if (_bypassDomain is EqualUnmodifiableListView) return _bypassDomain; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_bypassDomain); + } @override String toString() { - return 'VpnProps(enable: $enable, systemProxy: $systemProxy, ipv6: $ipv6, allowBypass: $allowBypass)'; + return 'VpnProps(enable: $enable, systemProxy: $systemProxy, ipv6: $ipv6, allowBypass: $allowBypass, bypassDomain: $bypassDomain)'; } @override @@ -1099,13 +1097,15 @@ class _$VpnPropsImpl implements _VpnProps { other.systemProxy == systemProxy) && (identical(other.ipv6, ipv6) || other.ipv6 == ipv6) && (identical(other.allowBypass, allowBypass) || - other.allowBypass == allowBypass)); + other.allowBypass == allowBypass) && + const DeepCollectionEquality() + .equals(other._bypassDomain, _bypassDomain)); } @JsonKey(ignore: true) @override - int get hashCode => - Object.hash(runtimeType, enable, systemProxy, ipv6, allowBypass); + int get hashCode => Object.hash(runtimeType, enable, systemProxy, ipv6, + allowBypass, const DeepCollectionEquality().hash(_bypassDomain)); @JsonKey(ignore: true) @override @@ -1126,7 +1126,8 @@ abstract class _VpnProps implements VpnProps { {final bool enable, final bool systemProxy, final bool ipv6, - final bool allowBypass}) = _$VpnPropsImpl; + final bool allowBypass, + final List bypassDomain}) = _$VpnPropsImpl; factory _VpnProps.fromJson(Map json) = _$VpnPropsImpl.fromJson; @@ -1140,6 +1141,8 @@ abstract class _VpnProps implements VpnProps { @override bool get allowBypass; @override + List get bypassDomain; + @override @JsonKey(ignore: true) _$$VpnPropsImplCopyWith<_$VpnPropsImpl> get copyWith => throw _privateConstructorUsedError; @@ -1283,34 +1286,44 @@ abstract class _DesktopProps implements DesktopProps { throw _privateConstructorUsedError; } -ScaleProps _$ScalePropsFromJson(Map json) { - return _ScaleProps.fromJson(json); +ProxiesStyle _$ProxiesStyleFromJson(Map json) { + return _ProxiesStyle.fromJson(json); } /// @nodoc -mixin _$ScaleProps { - bool get custom => throw _privateConstructorUsedError; - double get scale => throw _privateConstructorUsedError; +mixin _$ProxiesStyle { + ProxiesType get type => throw _privateConstructorUsedError; + ProxiesSortType get sortType => throw _privateConstructorUsedError; + ProxiesLayout get layout => throw _privateConstructorUsedError; + ProxiesIconStyle get iconStyle => throw _privateConstructorUsedError; + ProxyCardType get cardType => throw _privateConstructorUsedError; + Map get iconMap => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $ScalePropsCopyWith get copyWith => + $ProxiesStyleCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $ScalePropsCopyWith<$Res> { - factory $ScalePropsCopyWith( - ScaleProps value, $Res Function(ScaleProps) then) = - _$ScalePropsCopyWithImpl<$Res, ScaleProps>; +abstract class $ProxiesStyleCopyWith<$Res> { + factory $ProxiesStyleCopyWith( + ProxiesStyle value, $Res Function(ProxiesStyle) then) = + _$ProxiesStyleCopyWithImpl<$Res, ProxiesStyle>; @useResult - $Res call({bool custom, double scale}); + $Res call( + {ProxiesType type, + ProxiesSortType sortType, + ProxiesLayout layout, + ProxiesIconStyle iconStyle, + ProxyCardType cardType, + Map iconMap}); } /// @nodoc -class _$ScalePropsCopyWithImpl<$Res, $Val extends ScaleProps> - implements $ScalePropsCopyWith<$Res> { - _$ScalePropsCopyWithImpl(this._value, this._then); +class _$ProxiesStyleCopyWithImpl<$Res, $Val extends ProxiesStyle> + implements $ProxiesStyleCopyWith<$Res> { + _$ProxiesStyleCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; @@ -1320,121 +1333,211 @@ class _$ScalePropsCopyWithImpl<$Res, $Val extends ScaleProps> @pragma('vm:prefer-inline') @override $Res call({ - Object? custom = null, - Object? scale = null, + Object? type = null, + Object? sortType = null, + Object? layout = null, + Object? iconStyle = null, + Object? cardType = null, + Object? iconMap = null, }) { return _then(_value.copyWith( - custom: null == custom - ? _value.custom - : custom // ignore: cast_nullable_to_non_nullable - as bool, - scale: null == scale - ? _value.scale - : scale // ignore: cast_nullable_to_non_nullable - as double, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as ProxiesType, + sortType: null == sortType + ? _value.sortType + : sortType // ignore: cast_nullable_to_non_nullable + as ProxiesSortType, + layout: null == layout + ? _value.layout + : layout // ignore: cast_nullable_to_non_nullable + as ProxiesLayout, + iconStyle: null == iconStyle + ? _value.iconStyle + : iconStyle // ignore: cast_nullable_to_non_nullable + as ProxiesIconStyle, + cardType: null == cardType + ? _value.cardType + : cardType // ignore: cast_nullable_to_non_nullable + as ProxyCardType, + iconMap: null == iconMap + ? _value.iconMap + : iconMap // ignore: cast_nullable_to_non_nullable + as Map, ) as $Val); } } /// @nodoc -abstract class _$$ScalePropsImplCopyWith<$Res> - implements $ScalePropsCopyWith<$Res> { - factory _$$ScalePropsImplCopyWith( - _$ScalePropsImpl value, $Res Function(_$ScalePropsImpl) then) = - __$$ScalePropsImplCopyWithImpl<$Res>; +abstract class _$$ProxiesStyleImplCopyWith<$Res> + implements $ProxiesStyleCopyWith<$Res> { + factory _$$ProxiesStyleImplCopyWith( + _$ProxiesStyleImpl value, $Res Function(_$ProxiesStyleImpl) then) = + __$$ProxiesStyleImplCopyWithImpl<$Res>; @override @useResult - $Res call({bool custom, double scale}); + $Res call( + {ProxiesType type, + ProxiesSortType sortType, + ProxiesLayout layout, + ProxiesIconStyle iconStyle, + ProxyCardType cardType, + Map iconMap}); } /// @nodoc -class __$$ScalePropsImplCopyWithImpl<$Res> - extends _$ScalePropsCopyWithImpl<$Res, _$ScalePropsImpl> - implements _$$ScalePropsImplCopyWith<$Res> { - __$$ScalePropsImplCopyWithImpl( - _$ScalePropsImpl _value, $Res Function(_$ScalePropsImpl) _then) +class __$$ProxiesStyleImplCopyWithImpl<$Res> + extends _$ProxiesStyleCopyWithImpl<$Res, _$ProxiesStyleImpl> + implements _$$ProxiesStyleImplCopyWith<$Res> { + __$$ProxiesStyleImplCopyWithImpl( + _$ProxiesStyleImpl _value, $Res Function(_$ProxiesStyleImpl) _then) : super(_value, _then); @pragma('vm:prefer-inline') @override $Res call({ - Object? custom = null, - Object? scale = null, + Object? type = null, + Object? sortType = null, + Object? layout = null, + Object? iconStyle = null, + Object? cardType = null, + Object? iconMap = null, }) { - return _then(_$ScalePropsImpl( - custom: null == custom - ? _value.custom - : custom // ignore: cast_nullable_to_non_nullable - as bool, - scale: null == scale - ? _value.scale - : scale // ignore: cast_nullable_to_non_nullable - as double, + return _then(_$ProxiesStyleImpl( + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as ProxiesType, + sortType: null == sortType + ? _value.sortType + : sortType // ignore: cast_nullable_to_non_nullable + as ProxiesSortType, + layout: null == layout + ? _value.layout + : layout // ignore: cast_nullable_to_non_nullable + as ProxiesLayout, + iconStyle: null == iconStyle + ? _value.iconStyle + : iconStyle // ignore: cast_nullable_to_non_nullable + as ProxiesIconStyle, + cardType: null == cardType + ? _value.cardType + : cardType // ignore: cast_nullable_to_non_nullable + as ProxyCardType, + iconMap: null == iconMap + ? _value._iconMap + : iconMap // ignore: cast_nullable_to_non_nullable + as Map, )); } } /// @nodoc @JsonSerializable() -class _$ScalePropsImpl implements _ScaleProps { - const _$ScalePropsImpl( - {this.custom = false, this.scale = defaultCustomFontSizeScale}); +class _$ProxiesStyleImpl implements _ProxiesStyle { + const _$ProxiesStyleImpl( + {this.type = ProxiesType.tab, + this.sortType = ProxiesSortType.none, + this.layout = ProxiesLayout.standard, + this.iconStyle = ProxiesIconStyle.standard, + this.cardType = ProxyCardType.expand, + final Map iconMap = const {}}) + : _iconMap = iconMap; - factory _$ScalePropsImpl.fromJson(Map json) => - _$$ScalePropsImplFromJson(json); + factory _$ProxiesStyleImpl.fromJson(Map json) => + _$$ProxiesStyleImplFromJson(json); @override @JsonKey() - final bool custom; + final ProxiesType type; + @override + @JsonKey() + final ProxiesSortType sortType; + @override + @JsonKey() + final ProxiesLayout layout; + @override + @JsonKey() + final ProxiesIconStyle iconStyle; @override @JsonKey() - final double scale; + final ProxyCardType cardType; + final Map _iconMap; + @override + @JsonKey() + Map get iconMap { + if (_iconMap is EqualUnmodifiableMapView) return _iconMap; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_iconMap); + } @override String toString() { - return 'ScaleProps(custom: $custom, scale: $scale)'; + return 'ProxiesStyle(type: $type, sortType: $sortType, layout: $layout, iconStyle: $iconStyle, cardType: $cardType, iconMap: $iconMap)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$ScalePropsImpl && - (identical(other.custom, custom) || other.custom == custom) && - (identical(other.scale, scale) || other.scale == scale)); + other is _$ProxiesStyleImpl && + (identical(other.type, type) || other.type == type) && + (identical(other.sortType, sortType) || + other.sortType == sortType) && + (identical(other.layout, layout) || other.layout == layout) && + (identical(other.iconStyle, iconStyle) || + other.iconStyle == iconStyle) && + (identical(other.cardType, cardType) || + other.cardType == cardType) && + const DeepCollectionEquality().equals(other._iconMap, _iconMap)); } @JsonKey(ignore: true) @override - int get hashCode => Object.hash(runtimeType, custom, scale); + int get hashCode => Object.hash(runtimeType, type, sortType, layout, + iconStyle, cardType, const DeepCollectionEquality().hash(_iconMap)); @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$ScalePropsImplCopyWith<_$ScalePropsImpl> get copyWith => - __$$ScalePropsImplCopyWithImpl<_$ScalePropsImpl>(this, _$identity); + _$$ProxiesStyleImplCopyWith<_$ProxiesStyleImpl> get copyWith => + __$$ProxiesStyleImplCopyWithImpl<_$ProxiesStyleImpl>(this, _$identity); @override Map toJson() { - return _$$ScalePropsImplToJson( + return _$$ProxiesStyleImplToJson( this, ); } } -abstract class _ScaleProps implements ScaleProps { - const factory _ScaleProps({final bool custom, final double scale}) = - _$ScalePropsImpl; +abstract class _ProxiesStyle implements ProxiesStyle { + const factory _ProxiesStyle( + {final ProxiesType type, + final ProxiesSortType sortType, + final ProxiesLayout layout, + final ProxiesIconStyle iconStyle, + final ProxyCardType cardType, + final Map iconMap}) = _$ProxiesStyleImpl; - factory _ScaleProps.fromJson(Map json) = - _$ScalePropsImpl.fromJson; + factory _ProxiesStyle.fromJson(Map json) = + _$ProxiesStyleImpl.fromJson; @override - bool get custom; + ProxiesType get type; + @override + ProxiesSortType get sortType; + @override + ProxiesLayout get layout; + @override + ProxiesIconStyle get iconStyle; + @override + ProxyCardType get cardType; @override - double get scale; + Map get iconMap; @override @JsonKey(ignore: true) - _$$ScalePropsImplCopyWith<_$ScalePropsImpl> get copyWith => + _$$ProxiesStyleImplCopyWith<_$ProxiesStyleImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/models/generated/config.g.dart b/lib/models/generated/config.g.dart index 967920fd..4118f751 100644 --- a/lib/models/generated/config.g.dart +++ b/lib/models/generated/config.g.dart @@ -7,96 +7,52 @@ part of '../config.dart'; // ************************************************************************** Config _$ConfigFromJson(Map json) => Config() + ..appSetting = + AppSetting.realFromJson(json['appSetting'] as Map?) ..profiles = (json['profiles'] as List?) ?.map((e) => Profile.fromJson(e as Map)) .toList() ?? [] ..currentProfileId = json['currentProfileId'] as String? - ..autoLaunch = json['autoLaunch'] as bool? ?? false - ..silentLaunch = json['silentLaunch'] as bool? ?? false - ..autoRun = json['autoRun'] as bool? ?? false ..themeMode = $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ?? ThemeMode.system - ..openLogs = json['openLogs'] as bool? ?? false - ..locale = json['locale'] as String? ..primaryColor = (json['primaryColor'] as num?)?.toInt() - ..proxiesSortType = - $enumDecodeNullable(_$ProxiesSortTypeEnumMap, json['proxiesSortType']) ?? - ProxiesSortType.none - ..proxiesLayout = - $enumDecodeNullable(_$ProxiesLayoutEnumMap, json['proxiesLayout']) ?? - ProxiesLayout.standard - ..isMinimizeOnExit = json['isMinimizeOnExit'] as bool? ?? true ..isAccessControl = json['isAccessControl'] as bool? ?? false ..accessControl = AccessControl.fromJson(json['accessControl'] as Map) ..dav = json['dav'] == null ? null : DAV.fromJson(json['dav'] as Map) - ..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true - ..isCompatible = json['isCompatible'] as bool? ?? true - ..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true - ..onlyProxy = json['onlyProxy'] as bool? ?? false ..prueBlack = json['prueBlack'] as bool? ?? false - ..isCloseConnections = json['isCloseConnections'] as bool? ?? false - ..proxiesType = $enumDecodeNullable(_$ProxiesTypeEnumMap, json['proxiesType'], - unknownValue: ProxiesType.tab) ?? - ProxiesType.tab - ..proxyCardType = - $enumDecodeNullable(_$ProxyCardTypeEnumMap, json['proxyCardType']) ?? - ProxyCardType.expand - ..testUrl = - json['test-url'] as String? ?? 'https://www.gstatic.com/generate_204' - ..isExclude = json['isExclude'] as bool? ?? false ..windowProps = WindowProps.fromJson(json['windowProps'] as Map?) ..vpnProps = VpnProps.fromJson(json['vpnProps'] as Map?) ..desktopProps = DesktopProps.fromJson(json['desktopProps'] as Map?) - ..scaleProps = - ScaleProps.fromJson(json['scaleProps'] as Map?) - ..showLabel = json['showLabel'] as bool? ?? false ..overrideDns = json['overrideDns'] as bool? ?? false - ..isDisclaimerAccepted = json['isDisclaimerAccepted'] as bool? ?? false ..hotKeyActions = (json['hotKeyActions'] as List?) ?.map((e) => HotKeyAction.fromJson(e as Map)) .toList() ?? - []; + [] + ..proxiesStyle = + ProxiesStyle.fromJson(json['proxiesStyle'] as Map?); Map _$ConfigToJson(Config instance) => { + 'appSetting': instance.appSetting, 'profiles': instance.profiles, 'currentProfileId': instance.currentProfileId, - 'autoLaunch': instance.autoLaunch, - 'silentLaunch': instance.silentLaunch, - 'autoRun': instance.autoRun, 'themeMode': _$ThemeModeEnumMap[instance.themeMode]!, - 'openLogs': instance.openLogs, - 'locale': instance.locale, 'primaryColor': instance.primaryColor, - 'proxiesSortType': _$ProxiesSortTypeEnumMap[instance.proxiesSortType]!, - 'proxiesLayout': _$ProxiesLayoutEnumMap[instance.proxiesLayout]!, - 'isMinimizeOnExit': instance.isMinimizeOnExit, 'isAccessControl': instance.isAccessControl, 'accessControl': instance.accessControl, 'dav': instance.dav, - 'isAnimateToPage': instance.isAnimateToPage, - 'isCompatible': instance.isCompatible, - 'autoCheckUpdate': instance.autoCheckUpdate, - 'onlyProxy': instance.onlyProxy, 'prueBlack': instance.prueBlack, - 'isCloseConnections': instance.isCloseConnections, - 'proxiesType': _$ProxiesTypeEnumMap[instance.proxiesType]!, - 'proxyCardType': _$ProxyCardTypeEnumMap[instance.proxyCardType]!, - 'test-url': instance.testUrl, - 'isExclude': instance.isExclude, 'windowProps': instance.windowProps, 'vpnProps': instance.vpnProps, 'desktopProps': instance.desktopProps, - 'scaleProps': instance.scaleProps, - 'showLabel': instance.showLabel, 'overrideDns': instance.overrideDns, - 'isDisclaimerAccepted': instance.isDisclaimerAccepted, 'hotKeyActions': instance.hotKeyActions, + 'proxiesStyle': instance.proxiesStyle, }; const _$ThemeModeEnumMap = { @@ -105,28 +61,43 @@ const _$ThemeModeEnumMap = { ThemeMode.dark: 'dark', }; -const _$ProxiesSortTypeEnumMap = { - ProxiesSortType.none: 'none', - ProxiesSortType.delay: 'delay', - ProxiesSortType.name: 'name', -}; - -const _$ProxiesLayoutEnumMap = { - ProxiesLayout.loose: 'loose', - ProxiesLayout.standard: 'standard', - ProxiesLayout.tight: 'tight', -}; - -const _$ProxiesTypeEnumMap = { - ProxiesType.tab: 'tab', - ProxiesType.list: 'list', -}; +_$AppSettingImpl _$$AppSettingImplFromJson(Map json) => + _$AppSettingImpl( + locale: json['locale'] as String?, + onlyProxy: json['onlyProxy'] as bool? ?? false, + autoLaunch: json['autoLaunch'] as bool? ?? false, + adminAutoLaunch: json['adminAutoLaunch'] as bool? ?? false, + silentLaunch: json['silentLaunch'] as bool? ?? false, + autoRun: json['autoRun'] as bool? ?? false, + openLogs: json['openLogs'] as bool? ?? false, + closeConnections: json['closeConnections'] as bool? ?? true, + testUrl: json['testUrl'] as String? ?? defaultTestUrl, + isAnimateToPage: json['isAnimateToPage'] as bool? ?? true, + autoCheckUpdate: json['autoCheckUpdate'] as bool? ?? true, + showLabel: json['showLabel'] as bool? ?? false, + disclaimerAccepted: json['disclaimerAccepted'] as bool? ?? false, + minimizeOnExit: json['minimizeOnExit'] as bool? ?? true, + hidden: json['hidden'] as bool? ?? false, + ); -const _$ProxyCardTypeEnumMap = { - ProxyCardType.expand: 'expand', - ProxyCardType.shrink: 'shrink', - ProxyCardType.min: 'min', -}; +Map _$$AppSettingImplToJson(_$AppSettingImpl instance) => + { + 'locale': instance.locale, + 'onlyProxy': instance.onlyProxy, + 'autoLaunch': instance.autoLaunch, + 'adminAutoLaunch': instance.adminAutoLaunch, + 'silentLaunch': instance.silentLaunch, + 'autoRun': instance.autoRun, + 'openLogs': instance.openLogs, + 'closeConnections': instance.closeConnections, + 'testUrl': instance.testUrl, + 'isAnimateToPage': instance.isAnimateToPage, + 'autoCheckUpdate': instance.autoCheckUpdate, + 'showLabel': instance.showLabel, + 'disclaimerAccepted': instance.disclaimerAccepted, + 'minimizeOnExit': instance.minimizeOnExit, + 'hidden': instance.hidden, + }; _$AccessControlImpl _$$AccessControlImplFromJson(Map json) => _$AccessControlImpl( @@ -165,48 +136,6 @@ const _$AccessSortTypeEnumMap = { AccessSortType.time: 'time', }; -_$CoreStateImpl _$$CoreStateImplFromJson(Map json) => - _$CoreStateImpl( - accessControl: json['accessControl'] == null - ? null - : AccessControl.fromJson( - json['accessControl'] as Map), - currentProfileName: json['currentProfileName'] as String, - enable: json['enable'] as bool, - allowBypass: json['allowBypass'] as bool, - systemProxy: json['systemProxy'] as bool, - mixedPort: (json['mixedPort'] as num).toInt(), - ipv6: json['ipv6'] as bool, - onlyProxy: json['onlyProxy'] as bool, - ); - -Map _$$CoreStateImplToJson(_$CoreStateImpl instance) => - { - 'accessControl': instance.accessControl, - 'currentProfileName': instance.currentProfileName, - 'enable': instance.enable, - 'allowBypass': instance.allowBypass, - 'systemProxy': instance.systemProxy, - 'mixedPort': instance.mixedPort, - 'ipv6': instance.ipv6, - 'onlyProxy': instance.onlyProxy, - }; - -_$VPNStateImpl _$$VPNStateImplFromJson(Map json) => - _$VPNStateImpl( - accessControl: json['accessControl'] == null - ? null - : AccessControl.fromJson( - json['accessControl'] as Map), - vpnProps: VpnProps.fromJson(json['vpnProps'] as Map?), - ); - -Map _$$VPNStateImplToJson(_$VPNStateImpl instance) => - { - 'accessControl': instance.accessControl, - 'vpnProps': instance.vpnProps, - }; - _$WindowPropsImpl _$$WindowPropsImplFromJson(Map json) => _$WindowPropsImpl( width: (json['width'] as num?)?.toDouble() ?? 1000, @@ -229,6 +158,10 @@ _$VpnPropsImpl _$$VpnPropsImplFromJson(Map json) => systemProxy: json['systemProxy'] as bool? ?? true, ipv6: json['ipv6'] as bool? ?? false, allowBypass: json['allowBypass'] as bool? ?? true, + bypassDomain: (json['bypassDomain'] as List?) + ?.map((e) => e as String) + .toList() ?? + defaultBypassDomain, ); Map _$$VpnPropsImplToJson(_$VpnPropsImpl instance) => @@ -237,6 +170,7 @@ Map _$$VpnPropsImplToJson(_$VpnPropsImpl instance) => 'systemProxy': instance.systemProxy, 'ipv6': instance.ipv6, 'allowBypass': instance.allowBypass, + 'bypassDomain': instance.bypassDomain, }; _$DesktopPropsImpl _$$DesktopPropsImplFromJson(Map json) => @@ -249,14 +183,61 @@ Map _$$DesktopPropsImplToJson(_$DesktopPropsImpl instance) => 'systemProxy': instance.systemProxy, }; -_$ScalePropsImpl _$$ScalePropsImplFromJson(Map json) => - _$ScalePropsImpl( - custom: json['custom'] as bool? ?? false, - scale: (json['scale'] as num?)?.toDouble() ?? defaultCustomFontSizeScale, +_$ProxiesStyleImpl _$$ProxiesStyleImplFromJson(Map json) => + _$ProxiesStyleImpl( + type: $enumDecodeNullable(_$ProxiesTypeEnumMap, json['type']) ?? + ProxiesType.tab, + sortType: + $enumDecodeNullable(_$ProxiesSortTypeEnumMap, json['sortType']) ?? + ProxiesSortType.none, + layout: $enumDecodeNullable(_$ProxiesLayoutEnumMap, json['layout']) ?? + ProxiesLayout.standard, + iconStyle: + $enumDecodeNullable(_$ProxiesIconStyleEnumMap, json['iconStyle']) ?? + ProxiesIconStyle.standard, + cardType: $enumDecodeNullable(_$ProxyCardTypeEnumMap, json['cardType']) ?? + ProxyCardType.expand, + iconMap: (json['iconMap'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ) ?? + const {}, ); -Map _$$ScalePropsImplToJson(_$ScalePropsImpl instance) => +Map _$$ProxiesStyleImplToJson(_$ProxiesStyleImpl instance) => { - 'custom': instance.custom, - 'scale': instance.scale, + 'type': _$ProxiesTypeEnumMap[instance.type]!, + 'sortType': _$ProxiesSortTypeEnumMap[instance.sortType]!, + 'layout': _$ProxiesLayoutEnumMap[instance.layout]!, + 'iconStyle': _$ProxiesIconStyleEnumMap[instance.iconStyle]!, + 'cardType': _$ProxyCardTypeEnumMap[instance.cardType]!, + 'iconMap': instance.iconMap, }; + +const _$ProxiesTypeEnumMap = { + ProxiesType.tab: 'tab', + ProxiesType.list: 'list', +}; + +const _$ProxiesSortTypeEnumMap = { + ProxiesSortType.none: 'none', + ProxiesSortType.delay: 'delay', + ProxiesSortType.name: 'name', +}; + +const _$ProxiesLayoutEnumMap = { + ProxiesLayout.loose: 'loose', + ProxiesLayout.standard: 'standard', + ProxiesLayout.tight: 'tight', +}; + +const _$ProxiesIconStyleEnumMap = { + ProxiesIconStyle.standard: 'standard', + ProxiesIconStyle.none: 'none', + ProxiesIconStyle.icon: 'icon', +}; + +const _$ProxyCardTypeEnumMap = { + ProxyCardType.expand: 'expand', + ProxyCardType.shrink: 'shrink', + ProxyCardType.min: 'min', +}; diff --git a/lib/models/generated/ffi.freezed.dart b/lib/models/generated/ffi.freezed.dart index 087c3d7f..d2bb34db 100644 --- a/lib/models/generated/ffi.freezed.dart +++ b/lib/models/generated/ffi.freezed.dart @@ -14,6 +14,666 @@ T _$identity(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'); +CoreState _$CoreStateFromJson(Map json) { + return _CoreState.fromJson(json); +} + +/// @nodoc +mixin _$CoreState { + bool get enable => throw _privateConstructorUsedError; + AccessControl? get accessControl => throw _privateConstructorUsedError; + String get currentProfileName => throw _privateConstructorUsedError; + bool get allowBypass => throw _privateConstructorUsedError; + bool get systemProxy => throw _privateConstructorUsedError; + List get bypassDomain => throw _privateConstructorUsedError; + bool get ipv6 => throw _privateConstructorUsedError; + bool get onlyProxy => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $CoreStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CoreStateCopyWith<$Res> { + factory $CoreStateCopyWith(CoreState value, $Res Function(CoreState) then) = + _$CoreStateCopyWithImpl<$Res, CoreState>; + @useResult + $Res call( + {bool enable, + AccessControl? accessControl, + String currentProfileName, + bool allowBypass, + bool systemProxy, + List bypassDomain, + bool ipv6, + bool onlyProxy}); + + $AccessControlCopyWith<$Res>? get accessControl; +} + +/// @nodoc +class _$CoreStateCopyWithImpl<$Res, $Val extends CoreState> + implements $CoreStateCopyWith<$Res> { + _$CoreStateCopyWithImpl(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? enable = null, + Object? accessControl = freezed, + Object? currentProfileName = null, + Object? allowBypass = null, + Object? systemProxy = null, + Object? bypassDomain = null, + Object? ipv6 = null, + Object? onlyProxy = null, + }) { + return _then(_value.copyWith( + enable: null == enable + ? _value.enable + : enable // ignore: cast_nullable_to_non_nullable + as bool, + accessControl: freezed == accessControl + ? _value.accessControl + : accessControl // ignore: cast_nullable_to_non_nullable + as AccessControl?, + currentProfileName: null == currentProfileName + ? _value.currentProfileName + : currentProfileName // ignore: cast_nullable_to_non_nullable + as String, + allowBypass: null == allowBypass + ? _value.allowBypass + : allowBypass // ignore: cast_nullable_to_non_nullable + as bool, + systemProxy: null == systemProxy + ? _value.systemProxy + : systemProxy // ignore: cast_nullable_to_non_nullable + as bool, + bypassDomain: null == bypassDomain + ? _value.bypassDomain + : bypassDomain // ignore: cast_nullable_to_non_nullable + as List, + ipv6: null == ipv6 + ? _value.ipv6 + : ipv6 // ignore: cast_nullable_to_non_nullable + as bool, + onlyProxy: null == onlyProxy + ? _value.onlyProxy + : onlyProxy // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $AccessControlCopyWith<$Res>? get accessControl { + if (_value.accessControl == null) { + return null; + } + + return $AccessControlCopyWith<$Res>(_value.accessControl!, (value) { + return _then(_value.copyWith(accessControl: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$CoreStateImplCopyWith<$Res> + implements $CoreStateCopyWith<$Res> { + factory _$$CoreStateImplCopyWith( + _$CoreStateImpl value, $Res Function(_$CoreStateImpl) then) = + __$$CoreStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {bool enable, + AccessControl? accessControl, + String currentProfileName, + bool allowBypass, + bool systemProxy, + List bypassDomain, + bool ipv6, + bool onlyProxy}); + + @override + $AccessControlCopyWith<$Res>? get accessControl; +} + +/// @nodoc +class __$$CoreStateImplCopyWithImpl<$Res> + extends _$CoreStateCopyWithImpl<$Res, _$CoreStateImpl> + implements _$$CoreStateImplCopyWith<$Res> { + __$$CoreStateImplCopyWithImpl( + _$CoreStateImpl _value, $Res Function(_$CoreStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? enable = null, + Object? accessControl = freezed, + Object? currentProfileName = null, + Object? allowBypass = null, + Object? systemProxy = null, + Object? bypassDomain = null, + Object? ipv6 = null, + Object? onlyProxy = null, + }) { + return _then(_$CoreStateImpl( + enable: null == enable + ? _value.enable + : enable // ignore: cast_nullable_to_non_nullable + as bool, + accessControl: freezed == accessControl + ? _value.accessControl + : accessControl // ignore: cast_nullable_to_non_nullable + as AccessControl?, + currentProfileName: null == currentProfileName + ? _value.currentProfileName + : currentProfileName // ignore: cast_nullable_to_non_nullable + as String, + allowBypass: null == allowBypass + ? _value.allowBypass + : allowBypass // ignore: cast_nullable_to_non_nullable + as bool, + systemProxy: null == systemProxy + ? _value.systemProxy + : systemProxy // ignore: cast_nullable_to_non_nullable + as bool, + bypassDomain: null == bypassDomain + ? _value._bypassDomain + : bypassDomain // ignore: cast_nullable_to_non_nullable + as List, + ipv6: null == ipv6 + ? _value.ipv6 + : ipv6 // ignore: cast_nullable_to_non_nullable + as bool, + onlyProxy: null == onlyProxy + ? _value.onlyProxy + : onlyProxy // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$CoreStateImpl implements _CoreState { + const _$CoreStateImpl( + {required this.enable, + this.accessControl, + required this.currentProfileName, + required this.allowBypass, + required this.systemProxy, + required final List bypassDomain, + required this.ipv6, + required this.onlyProxy}) + : _bypassDomain = bypassDomain; + + factory _$CoreStateImpl.fromJson(Map json) => + _$$CoreStateImplFromJson(json); + + @override + final bool enable; + @override + final AccessControl? accessControl; + @override + final String currentProfileName; + @override + final bool allowBypass; + @override + final bool systemProxy; + final List _bypassDomain; + @override + List get bypassDomain { + if (_bypassDomain is EqualUnmodifiableListView) return _bypassDomain; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_bypassDomain); + } + + @override + final bool ipv6; + @override + final bool onlyProxy; + + @override + String toString() { + return 'CoreState(enable: $enable, accessControl: $accessControl, currentProfileName: $currentProfileName, allowBypass: $allowBypass, systemProxy: $systemProxy, bypassDomain: $bypassDomain, ipv6: $ipv6, onlyProxy: $onlyProxy)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$CoreStateImpl && + (identical(other.enable, enable) || other.enable == enable) && + (identical(other.accessControl, accessControl) || + other.accessControl == accessControl) && + (identical(other.currentProfileName, currentProfileName) || + other.currentProfileName == currentProfileName) && + (identical(other.allowBypass, allowBypass) || + other.allowBypass == allowBypass) && + (identical(other.systemProxy, systemProxy) || + other.systemProxy == systemProxy) && + const DeepCollectionEquality() + .equals(other._bypassDomain, _bypassDomain) && + (identical(other.ipv6, ipv6) || other.ipv6 == ipv6) && + (identical(other.onlyProxy, onlyProxy) || + other.onlyProxy == onlyProxy)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + enable, + accessControl, + currentProfileName, + allowBypass, + systemProxy, + const DeepCollectionEquality().hash(_bypassDomain), + ipv6, + onlyProxy); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$CoreStateImplCopyWith<_$CoreStateImpl> get copyWith => + __$$CoreStateImplCopyWithImpl<_$CoreStateImpl>(this, _$identity); + + @override + Map toJson() { + return _$$CoreStateImplToJson( + this, + ); + } +} + +abstract class _CoreState implements CoreState { + const factory _CoreState( + {required final bool enable, + final AccessControl? accessControl, + required final String currentProfileName, + required final bool allowBypass, + required final bool systemProxy, + required final List bypassDomain, + required final bool ipv6, + required final bool onlyProxy}) = _$CoreStateImpl; + + factory _CoreState.fromJson(Map json) = + _$CoreStateImpl.fromJson; + + @override + bool get enable; + @override + AccessControl? get accessControl; + @override + String get currentProfileName; + @override + bool get allowBypass; + @override + bool get systemProxy; + @override + List get bypassDomain; + @override + bool get ipv6; + @override + bool get onlyProxy; + @override + @JsonKey(ignore: true) + _$$CoreStateImplCopyWith<_$CoreStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +AndroidVpnOptions _$AndroidVpnOptionsFromJson(Map json) { + return _AndroidVpnOptions.fromJson(json); +} + +/// @nodoc +mixin _$AndroidVpnOptions { + bool get enable => throw _privateConstructorUsedError; + int get port => throw _privateConstructorUsedError; + AccessControl? get accessControl => throw _privateConstructorUsedError; + bool get allowBypass => throw _privateConstructorUsedError; + bool get systemProxy => throw _privateConstructorUsedError; + List get bypassDomain => throw _privateConstructorUsedError; + String get ipv4Address => throw _privateConstructorUsedError; + String get ipv6Address => throw _privateConstructorUsedError; + String get dnsServerAddress => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $AndroidVpnOptionsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AndroidVpnOptionsCopyWith<$Res> { + factory $AndroidVpnOptionsCopyWith( + AndroidVpnOptions value, $Res Function(AndroidVpnOptions) then) = + _$AndroidVpnOptionsCopyWithImpl<$Res, AndroidVpnOptions>; + @useResult + $Res call( + {bool enable, + int port, + AccessControl? accessControl, + bool allowBypass, + bool systemProxy, + List bypassDomain, + String ipv4Address, + String ipv6Address, + String dnsServerAddress}); + + $AccessControlCopyWith<$Res>? get accessControl; +} + +/// @nodoc +class _$AndroidVpnOptionsCopyWithImpl<$Res, $Val extends AndroidVpnOptions> + implements $AndroidVpnOptionsCopyWith<$Res> { + _$AndroidVpnOptionsCopyWithImpl(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? enable = null, + Object? port = null, + Object? accessControl = freezed, + Object? allowBypass = null, + Object? systemProxy = null, + Object? bypassDomain = null, + Object? ipv4Address = null, + Object? ipv6Address = null, + Object? dnsServerAddress = null, + }) { + return _then(_value.copyWith( + enable: null == enable + ? _value.enable + : enable // ignore: cast_nullable_to_non_nullable + as bool, + port: null == port + ? _value.port + : port // ignore: cast_nullable_to_non_nullable + as int, + accessControl: freezed == accessControl + ? _value.accessControl + : accessControl // ignore: cast_nullable_to_non_nullable + as AccessControl?, + allowBypass: null == allowBypass + ? _value.allowBypass + : allowBypass // ignore: cast_nullable_to_non_nullable + as bool, + systemProxy: null == systemProxy + ? _value.systemProxy + : systemProxy // ignore: cast_nullable_to_non_nullable + as bool, + bypassDomain: null == bypassDomain + ? _value.bypassDomain + : bypassDomain // ignore: cast_nullable_to_non_nullable + as List, + ipv4Address: null == ipv4Address + ? _value.ipv4Address + : ipv4Address // ignore: cast_nullable_to_non_nullable + as String, + ipv6Address: null == ipv6Address + ? _value.ipv6Address + : ipv6Address // ignore: cast_nullable_to_non_nullable + as String, + dnsServerAddress: null == dnsServerAddress + ? _value.dnsServerAddress + : dnsServerAddress // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $AccessControlCopyWith<$Res>? get accessControl { + if (_value.accessControl == null) { + return null; + } + + return $AccessControlCopyWith<$Res>(_value.accessControl!, (value) { + return _then(_value.copyWith(accessControl: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$AndroidVpnOptionsImplCopyWith<$Res> + implements $AndroidVpnOptionsCopyWith<$Res> { + factory _$$AndroidVpnOptionsImplCopyWith(_$AndroidVpnOptionsImpl value, + $Res Function(_$AndroidVpnOptionsImpl) then) = + __$$AndroidVpnOptionsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {bool enable, + int port, + AccessControl? accessControl, + bool allowBypass, + bool systemProxy, + List bypassDomain, + String ipv4Address, + String ipv6Address, + String dnsServerAddress}); + + @override + $AccessControlCopyWith<$Res>? get accessControl; +} + +/// @nodoc +class __$$AndroidVpnOptionsImplCopyWithImpl<$Res> + extends _$AndroidVpnOptionsCopyWithImpl<$Res, _$AndroidVpnOptionsImpl> + implements _$$AndroidVpnOptionsImplCopyWith<$Res> { + __$$AndroidVpnOptionsImplCopyWithImpl(_$AndroidVpnOptionsImpl _value, + $Res Function(_$AndroidVpnOptionsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? enable = null, + Object? port = null, + Object? accessControl = freezed, + Object? allowBypass = null, + Object? systemProxy = null, + Object? bypassDomain = null, + Object? ipv4Address = null, + Object? ipv6Address = null, + Object? dnsServerAddress = null, + }) { + return _then(_$AndroidVpnOptionsImpl( + enable: null == enable + ? _value.enable + : enable // ignore: cast_nullable_to_non_nullable + as bool, + port: null == port + ? _value.port + : port // ignore: cast_nullable_to_non_nullable + as int, + accessControl: freezed == accessControl + ? _value.accessControl + : accessControl // ignore: cast_nullable_to_non_nullable + as AccessControl?, + allowBypass: null == allowBypass + ? _value.allowBypass + : allowBypass // ignore: cast_nullable_to_non_nullable + as bool, + systemProxy: null == systemProxy + ? _value.systemProxy + : systemProxy // ignore: cast_nullable_to_non_nullable + as bool, + bypassDomain: null == bypassDomain + ? _value._bypassDomain + : bypassDomain // ignore: cast_nullable_to_non_nullable + as List, + ipv4Address: null == ipv4Address + ? _value.ipv4Address + : ipv4Address // ignore: cast_nullable_to_non_nullable + as String, + ipv6Address: null == ipv6Address + ? _value.ipv6Address + : ipv6Address // ignore: cast_nullable_to_non_nullable + as String, + dnsServerAddress: null == dnsServerAddress + ? _value.dnsServerAddress + : dnsServerAddress // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$AndroidVpnOptionsImpl implements _AndroidVpnOptions { + const _$AndroidVpnOptionsImpl( + {required this.enable, + required this.port, + required this.accessControl, + required this.allowBypass, + required this.systemProxy, + required final List bypassDomain, + required this.ipv4Address, + required this.ipv6Address, + required this.dnsServerAddress}) + : _bypassDomain = bypassDomain; + + factory _$AndroidVpnOptionsImpl.fromJson(Map json) => + _$$AndroidVpnOptionsImplFromJson(json); + + @override + final bool enable; + @override + final int port; + @override + final AccessControl? accessControl; + @override + final bool allowBypass; + @override + final bool systemProxy; + final List _bypassDomain; + @override + List get bypassDomain { + if (_bypassDomain is EqualUnmodifiableListView) return _bypassDomain; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_bypassDomain); + } + + @override + final String ipv4Address; + @override + final String ipv6Address; + @override + final String dnsServerAddress; + + @override + String toString() { + return 'AndroidVpnOptions(enable: $enable, port: $port, accessControl: $accessControl, allowBypass: $allowBypass, systemProxy: $systemProxy, bypassDomain: $bypassDomain, ipv4Address: $ipv4Address, ipv6Address: $ipv6Address, dnsServerAddress: $dnsServerAddress)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AndroidVpnOptionsImpl && + (identical(other.enable, enable) || other.enable == enable) && + (identical(other.port, port) || other.port == port) && + (identical(other.accessControl, accessControl) || + other.accessControl == accessControl) && + (identical(other.allowBypass, allowBypass) || + other.allowBypass == allowBypass) && + (identical(other.systemProxy, systemProxy) || + other.systemProxy == systemProxy) && + const DeepCollectionEquality() + .equals(other._bypassDomain, _bypassDomain) && + (identical(other.ipv4Address, ipv4Address) || + other.ipv4Address == ipv4Address) && + (identical(other.ipv6Address, ipv6Address) || + other.ipv6Address == ipv6Address) && + (identical(other.dnsServerAddress, dnsServerAddress) || + other.dnsServerAddress == dnsServerAddress)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + enable, + port, + accessControl, + allowBypass, + systemProxy, + const DeepCollectionEquality().hash(_bypassDomain), + ipv4Address, + ipv6Address, + dnsServerAddress); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$AndroidVpnOptionsImplCopyWith<_$AndroidVpnOptionsImpl> get copyWith => + __$$AndroidVpnOptionsImplCopyWithImpl<_$AndroidVpnOptionsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$AndroidVpnOptionsImplToJson( + this, + ); + } +} + +abstract class _AndroidVpnOptions implements AndroidVpnOptions { + const factory _AndroidVpnOptions( + {required final bool enable, + required final int port, + required final AccessControl? accessControl, + required final bool allowBypass, + required final bool systemProxy, + required final List bypassDomain, + required final String ipv4Address, + required final String ipv6Address, + required final String dnsServerAddress}) = _$AndroidVpnOptionsImpl; + + factory _AndroidVpnOptions.fromJson(Map json) = + _$AndroidVpnOptionsImpl.fromJson; + + @override + bool get enable; + @override + int get port; + @override + AccessControl? get accessControl; + @override + bool get allowBypass; + @override + bool get systemProxy; + @override + List get bypassDomain; + @override + String get ipv4Address; + @override + String get ipv6Address; + @override + String get dnsServerAddress; + @override + @JsonKey(ignore: true) + _$$AndroidVpnOptionsImplCopyWith<_$AndroidVpnOptionsImpl> get copyWith => + throw _privateConstructorUsedError; +} + ConfigExtendedParams _$ConfigExtendedParamsFromJson(Map json) { return _ConfigExtendedParams.fromJson(json); } diff --git a/lib/models/generated/ffi.g.dart b/lib/models/generated/ffi.g.dart index eae4eb1c..cb4557a3 100644 --- a/lib/models/generated/ffi.g.dart +++ b/lib/models/generated/ffi.g.dart @@ -6,6 +6,68 @@ part of '../ffi.dart'; // JsonSerializableGenerator // ************************************************************************** +_$CoreStateImpl _$$CoreStateImplFromJson(Map json) => + _$CoreStateImpl( + enable: json['enable'] as bool, + accessControl: json['accessControl'] == null + ? null + : AccessControl.fromJson( + json['accessControl'] as Map), + currentProfileName: json['currentProfileName'] as String, + allowBypass: json['allowBypass'] as bool, + systemProxy: json['systemProxy'] as bool, + bypassDomain: (json['bypassDomain'] as List) + .map((e) => e as String) + .toList(), + ipv6: json['ipv6'] as bool, + onlyProxy: json['onlyProxy'] as bool, + ); + +Map _$$CoreStateImplToJson(_$CoreStateImpl instance) => + { + 'enable': instance.enable, + 'accessControl': instance.accessControl, + 'currentProfileName': instance.currentProfileName, + 'allowBypass': instance.allowBypass, + 'systemProxy': instance.systemProxy, + 'bypassDomain': instance.bypassDomain, + 'ipv6': instance.ipv6, + 'onlyProxy': instance.onlyProxy, + }; + +_$AndroidVpnOptionsImpl _$$AndroidVpnOptionsImplFromJson( + Map json) => + _$AndroidVpnOptionsImpl( + enable: json['enable'] as bool, + port: (json['port'] as num).toInt(), + accessControl: json['accessControl'] == null + ? null + : AccessControl.fromJson( + json['accessControl'] as Map), + allowBypass: json['allowBypass'] as bool, + systemProxy: json['systemProxy'] as bool, + bypassDomain: (json['bypassDomain'] as List) + .map((e) => e as String) + .toList(), + ipv4Address: json['ipv4Address'] as String, + ipv6Address: json['ipv6Address'] as String, + dnsServerAddress: json['dnsServerAddress'] as String, + ); + +Map _$$AndroidVpnOptionsImplToJson( + _$AndroidVpnOptionsImpl instance) => + { + 'enable': instance.enable, + 'port': instance.port, + 'accessControl': instance.accessControl, + 'allowBypass': instance.allowBypass, + 'systemProxy': instance.systemProxy, + 'bypassDomain': instance.bypassDomain, + 'ipv4Address': instance.ipv4Address, + 'ipv6Address': instance.ipv6Address, + 'dnsServerAddress': instance.dnsServerAddress, + }; + _$ConfigExtendedParamsImpl _$$ConfigExtendedParamsImplFromJson( Map json) => _$ConfigExtendedParamsImpl( diff --git a/lib/models/generated/selector.freezed.dart b/lib/models/generated/selector.freezed.dart index e1640f85..9c07ca0c 100644 --- a/lib/models/generated/selector.freezed.dart +++ b/lib/models/generated/selector.freezed.dart @@ -960,6 +960,7 @@ abstract class _ApplicationSelectorState implements ApplicationSelectorState { mixin _$TrayState { Mode get mode => throw _privateConstructorUsedError; bool get autoLaunch => throw _privateConstructorUsedError; + bool get adminAutoLaunch => throw _privateConstructorUsedError; bool get systemProxy => throw _privateConstructorUsedError; bool get tunEnable => throw _privateConstructorUsedError; bool get isStart => throw _privateConstructorUsedError; @@ -979,6 +980,7 @@ abstract class $TrayStateCopyWith<$Res> { $Res call( {Mode mode, bool autoLaunch, + bool adminAutoLaunch, bool systemProxy, bool tunEnable, bool isStart, @@ -1001,6 +1003,7 @@ class _$TrayStateCopyWithImpl<$Res, $Val extends TrayState> $Res call({ Object? mode = null, Object? autoLaunch = null, + Object? adminAutoLaunch = null, Object? systemProxy = null, Object? tunEnable = null, Object? isStart = null, @@ -1016,6 +1019,10 @@ class _$TrayStateCopyWithImpl<$Res, $Val extends TrayState> ? _value.autoLaunch : autoLaunch // ignore: cast_nullable_to_non_nullable as bool, + adminAutoLaunch: null == adminAutoLaunch + ? _value.adminAutoLaunch + : adminAutoLaunch // ignore: cast_nullable_to_non_nullable + as bool, systemProxy: null == systemProxy ? _value.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable @@ -1051,6 +1058,7 @@ abstract class _$$TrayStateImplCopyWith<$Res> $Res call( {Mode mode, bool autoLaunch, + bool adminAutoLaunch, bool systemProxy, bool tunEnable, bool isStart, @@ -1071,6 +1079,7 @@ class __$$TrayStateImplCopyWithImpl<$Res> $Res call({ Object? mode = null, Object? autoLaunch = null, + Object? adminAutoLaunch = null, Object? systemProxy = null, Object? tunEnable = null, Object? isStart = null, @@ -1086,6 +1095,10 @@ class __$$TrayStateImplCopyWithImpl<$Res> ? _value.autoLaunch : autoLaunch // ignore: cast_nullable_to_non_nullable as bool, + adminAutoLaunch: null == adminAutoLaunch + ? _value.adminAutoLaunch + : adminAutoLaunch // ignore: cast_nullable_to_non_nullable + as bool, systemProxy: null == systemProxy ? _value.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable @@ -1116,6 +1129,7 @@ class _$TrayStateImpl implements _TrayState { const _$TrayStateImpl( {required this.mode, required this.autoLaunch, + required this.adminAutoLaunch, required this.systemProxy, required this.tunEnable, required this.isStart, @@ -1127,6 +1141,8 @@ class _$TrayStateImpl implements _TrayState { @override final bool autoLaunch; @override + final bool adminAutoLaunch; + @override final bool systemProxy; @override final bool tunEnable; @@ -1139,7 +1155,7 @@ class _$TrayStateImpl implements _TrayState { @override String toString() { - return 'TrayState(mode: $mode, autoLaunch: $autoLaunch, systemProxy: $systemProxy, tunEnable: $tunEnable, isStart: $isStart, locale: $locale, brightness: $brightness)'; + return 'TrayState(mode: $mode, autoLaunch: $autoLaunch, adminAutoLaunch: $adminAutoLaunch, systemProxy: $systemProxy, tunEnable: $tunEnable, isStart: $isStart, locale: $locale, brightness: $brightness)'; } @override @@ -1150,6 +1166,8 @@ class _$TrayStateImpl implements _TrayState { (identical(other.mode, mode) || other.mode == mode) && (identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch) && + (identical(other.adminAutoLaunch, adminAutoLaunch) || + other.adminAutoLaunch == adminAutoLaunch) && (identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy) && (identical(other.tunEnable, tunEnable) || @@ -1161,8 +1179,8 @@ class _$TrayStateImpl implements _TrayState { } @override - int get hashCode => Object.hash(runtimeType, mode, autoLaunch, systemProxy, - tunEnable, isStart, locale, brightness); + int get hashCode => Object.hash(runtimeType, mode, autoLaunch, + adminAutoLaunch, systemProxy, tunEnable, isStart, locale, brightness); @JsonKey(ignore: true) @override @@ -1175,6 +1193,7 @@ abstract class _TrayState implements TrayState { const factory _TrayState( {required final Mode mode, required final bool autoLaunch, + required final bool adminAutoLaunch, required final bool systemProxy, required final bool tunEnable, required final bool isStart, @@ -1186,6 +1205,8 @@ abstract class _TrayState implements TrayState { @override bool get autoLaunch; @override + bool get adminAutoLaunch; + @override bool get systemProxy; @override bool get tunEnable; @@ -2919,7 +2940,7 @@ abstract class _ProxiesActionsState implements ProxiesActionsState { /// @nodoc mixin _$AutoLaunchState { bool get isAutoLaunch => throw _privateConstructorUsedError; - bool get isOpenTun => throw _privateConstructorUsedError; + bool get isAdminAutoLaunch => throw _privateConstructorUsedError; @JsonKey(ignore: true) $AutoLaunchStateCopyWith get copyWith => @@ -2932,7 +2953,7 @@ abstract class $AutoLaunchStateCopyWith<$Res> { AutoLaunchState value, $Res Function(AutoLaunchState) then) = _$AutoLaunchStateCopyWithImpl<$Res, AutoLaunchState>; @useResult - $Res call({bool isAutoLaunch, bool isOpenTun}); + $Res call({bool isAutoLaunch, bool isAdminAutoLaunch}); } /// @nodoc @@ -2949,16 +2970,16 @@ class _$AutoLaunchStateCopyWithImpl<$Res, $Val extends AutoLaunchState> @override $Res call({ Object? isAutoLaunch = null, - Object? isOpenTun = null, + Object? isAdminAutoLaunch = null, }) { return _then(_value.copyWith( isAutoLaunch: null == isAutoLaunch ? _value.isAutoLaunch : isAutoLaunch // ignore: cast_nullable_to_non_nullable as bool, - isOpenTun: null == isOpenTun - ? _value.isOpenTun - : isOpenTun // ignore: cast_nullable_to_non_nullable + isAdminAutoLaunch: null == isAdminAutoLaunch + ? _value.isAdminAutoLaunch + : isAdminAutoLaunch // ignore: cast_nullable_to_non_nullable as bool, ) as $Val); } @@ -2972,7 +2993,7 @@ abstract class _$$AutoLaunchStateImplCopyWith<$Res> __$$AutoLaunchStateImplCopyWithImpl<$Res>; @override @useResult - $Res call({bool isAutoLaunch, bool isOpenTun}); + $Res call({bool isAutoLaunch, bool isAdminAutoLaunch}); } /// @nodoc @@ -2987,16 +3008,16 @@ class __$$AutoLaunchStateImplCopyWithImpl<$Res> @override $Res call({ Object? isAutoLaunch = null, - Object? isOpenTun = null, + Object? isAdminAutoLaunch = null, }) { return _then(_$AutoLaunchStateImpl( isAutoLaunch: null == isAutoLaunch ? _value.isAutoLaunch : isAutoLaunch // ignore: cast_nullable_to_non_nullable as bool, - isOpenTun: null == isOpenTun - ? _value.isOpenTun - : isOpenTun // ignore: cast_nullable_to_non_nullable + isAdminAutoLaunch: null == isAdminAutoLaunch + ? _value.isAdminAutoLaunch + : isAdminAutoLaunch // ignore: cast_nullable_to_non_nullable as bool, )); } @@ -3006,16 +3027,16 @@ class __$$AutoLaunchStateImplCopyWithImpl<$Res> class _$AutoLaunchStateImpl implements _AutoLaunchState { const _$AutoLaunchStateImpl( - {required this.isAutoLaunch, required this.isOpenTun}); + {required this.isAutoLaunch, required this.isAdminAutoLaunch}); @override final bool isAutoLaunch; @override - final bool isOpenTun; + final bool isAdminAutoLaunch; @override String toString() { - return 'AutoLaunchState(isAutoLaunch: $isAutoLaunch, isOpenTun: $isOpenTun)'; + return 'AutoLaunchState(isAutoLaunch: $isAutoLaunch, isAdminAutoLaunch: $isAdminAutoLaunch)'; } @override @@ -3025,12 +3046,12 @@ class _$AutoLaunchStateImpl implements _AutoLaunchState { other is _$AutoLaunchStateImpl && (identical(other.isAutoLaunch, isAutoLaunch) || other.isAutoLaunch == isAutoLaunch) && - (identical(other.isOpenTun, isOpenTun) || - other.isOpenTun == isOpenTun)); + (identical(other.isAdminAutoLaunch, isAdminAutoLaunch) || + other.isAdminAutoLaunch == isAdminAutoLaunch)); } @override - int get hashCode => Object.hash(runtimeType, isAutoLaunch, isOpenTun); + int get hashCode => Object.hash(runtimeType, isAutoLaunch, isAdminAutoLaunch); @JsonKey(ignore: true) @override @@ -3043,12 +3064,12 @@ class _$AutoLaunchStateImpl implements _AutoLaunchState { abstract class _AutoLaunchState implements AutoLaunchState { const factory _AutoLaunchState( {required final bool isAutoLaunch, - required final bool isOpenTun}) = _$AutoLaunchStateImpl; + required final bool isAdminAutoLaunch}) = _$AutoLaunchStateImpl; @override bool get isAutoLaunch; @override - bool get isOpenTun; + bool get isAdminAutoLaunch; @override @JsonKey(ignore: true) _$$AutoLaunchStateImplCopyWith<_$AutoLaunchStateImpl> get copyWith => @@ -3883,30 +3904,31 @@ abstract class _ClashConfigState implements ClashConfigState { } /// @nodoc -mixin _$ThemeState { - String? get locale => throw _privateConstructorUsedError; - ScaleProps get scaleProps => throw _privateConstructorUsedError; +mixin _$VPNState { + AccessControl? get accessControl => throw _privateConstructorUsedError; + TunStack get stack => throw _privateConstructorUsedError; + VpnProps get vpnProps => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $ThemeStateCopyWith get copyWith => + $VPNStateCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $ThemeStateCopyWith<$Res> { - factory $ThemeStateCopyWith( - ThemeState value, $Res Function(ThemeState) then) = - _$ThemeStateCopyWithImpl<$Res, ThemeState>; +abstract class $VPNStateCopyWith<$Res> { + factory $VPNStateCopyWith(VPNState value, $Res Function(VPNState) then) = + _$VPNStateCopyWithImpl<$Res, VPNState>; @useResult - $Res call({String? locale, ScaleProps scaleProps}); + $Res call({AccessControl? accessControl, TunStack stack, VpnProps vpnProps}); - $ScalePropsCopyWith<$Res> get scaleProps; + $AccessControlCopyWith<$Res>? get accessControl; + $VpnPropsCopyWith<$Res> get vpnProps; } /// @nodoc -class _$ThemeStateCopyWithImpl<$Res, $Val extends ThemeState> - implements $ThemeStateCopyWith<$Res> { - _$ThemeStateCopyWithImpl(this._value, this._then); +class _$VPNStateCopyWithImpl<$Res, $Val extends VPNState> + implements $VPNStateCopyWith<$Res> { + _$VPNStateCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; @@ -3916,117 +3938,151 @@ class _$ThemeStateCopyWithImpl<$Res, $Val extends ThemeState> @pragma('vm:prefer-inline') @override $Res call({ - Object? locale = freezed, - Object? scaleProps = null, + Object? accessControl = freezed, + Object? stack = null, + Object? vpnProps = null, }) { return _then(_value.copyWith( - locale: freezed == locale - ? _value.locale - : locale // ignore: cast_nullable_to_non_nullable - as String?, - scaleProps: null == scaleProps - ? _value.scaleProps - : scaleProps // ignore: cast_nullable_to_non_nullable - as ScaleProps, + accessControl: freezed == accessControl + ? _value.accessControl + : accessControl // ignore: cast_nullable_to_non_nullable + as AccessControl?, + stack: null == stack + ? _value.stack + : stack // ignore: cast_nullable_to_non_nullable + as TunStack, + vpnProps: null == vpnProps + ? _value.vpnProps + : vpnProps // ignore: cast_nullable_to_non_nullable + as VpnProps, ) as $Val); } @override @pragma('vm:prefer-inline') - $ScalePropsCopyWith<$Res> get scaleProps { - return $ScalePropsCopyWith<$Res>(_value.scaleProps, (value) { - return _then(_value.copyWith(scaleProps: value) as $Val); + $AccessControlCopyWith<$Res>? get accessControl { + if (_value.accessControl == null) { + return null; + } + + return $AccessControlCopyWith<$Res>(_value.accessControl!, (value) { + return _then(_value.copyWith(accessControl: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $VpnPropsCopyWith<$Res> get vpnProps { + return $VpnPropsCopyWith<$Res>(_value.vpnProps, (value) { + return _then(_value.copyWith(vpnProps: value) as $Val); }); } } /// @nodoc -abstract class _$$ThemeStateImplCopyWith<$Res> - implements $ThemeStateCopyWith<$Res> { - factory _$$ThemeStateImplCopyWith( - _$ThemeStateImpl value, $Res Function(_$ThemeStateImpl) then) = - __$$ThemeStateImplCopyWithImpl<$Res>; +abstract class _$$VPNStateImplCopyWith<$Res> + implements $VPNStateCopyWith<$Res> { + factory _$$VPNStateImplCopyWith( + _$VPNStateImpl value, $Res Function(_$VPNStateImpl) then) = + __$$VPNStateImplCopyWithImpl<$Res>; @override @useResult - $Res call({String? locale, ScaleProps scaleProps}); + $Res call({AccessControl? accessControl, TunStack stack, VpnProps vpnProps}); @override - $ScalePropsCopyWith<$Res> get scaleProps; + $AccessControlCopyWith<$Res>? get accessControl; + @override + $VpnPropsCopyWith<$Res> get vpnProps; } /// @nodoc -class __$$ThemeStateImplCopyWithImpl<$Res> - extends _$ThemeStateCopyWithImpl<$Res, _$ThemeStateImpl> - implements _$$ThemeStateImplCopyWith<$Res> { - __$$ThemeStateImplCopyWithImpl( - _$ThemeStateImpl _value, $Res Function(_$ThemeStateImpl) _then) +class __$$VPNStateImplCopyWithImpl<$Res> + extends _$VPNStateCopyWithImpl<$Res, _$VPNStateImpl> + implements _$$VPNStateImplCopyWith<$Res> { + __$$VPNStateImplCopyWithImpl( + _$VPNStateImpl _value, $Res Function(_$VPNStateImpl) _then) : super(_value, _then); @pragma('vm:prefer-inline') @override $Res call({ - Object? locale = freezed, - Object? scaleProps = null, + Object? accessControl = freezed, + Object? stack = null, + Object? vpnProps = null, }) { - return _then(_$ThemeStateImpl( - locale: freezed == locale - ? _value.locale - : locale // ignore: cast_nullable_to_non_nullable - as String?, - scaleProps: null == scaleProps - ? _value.scaleProps - : scaleProps // ignore: cast_nullable_to_non_nullable - as ScaleProps, + return _then(_$VPNStateImpl( + accessControl: freezed == accessControl + ? _value.accessControl + : accessControl // ignore: cast_nullable_to_non_nullable + as AccessControl?, + stack: null == stack + ? _value.stack + : stack // ignore: cast_nullable_to_non_nullable + as TunStack, + vpnProps: null == vpnProps + ? _value.vpnProps + : vpnProps // ignore: cast_nullable_to_non_nullable + as VpnProps, )); } } /// @nodoc -class _$ThemeStateImpl implements _ThemeState { - const _$ThemeStateImpl({required this.locale, required this.scaleProps}); +class _$VPNStateImpl implements _VPNState { + const _$VPNStateImpl( + {required this.accessControl, + required this.stack, + required this.vpnProps}); @override - final String? locale; + final AccessControl? accessControl; + @override + final TunStack stack; @override - final ScaleProps scaleProps; + final VpnProps vpnProps; @override String toString() { - return 'ThemeState(locale: $locale, scaleProps: $scaleProps)'; + return 'VPNState(accessControl: $accessControl, stack: $stack, vpnProps: $vpnProps)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$ThemeStateImpl && - (identical(other.locale, locale) || other.locale == locale) && - (identical(other.scaleProps, scaleProps) || - other.scaleProps == scaleProps)); + other is _$VPNStateImpl && + (identical(other.accessControl, accessControl) || + other.accessControl == accessControl) && + (identical(other.stack, stack) || other.stack == stack) && + (identical(other.vpnProps, vpnProps) || + other.vpnProps == vpnProps)); } @override - int get hashCode => Object.hash(runtimeType, locale, scaleProps); + int get hashCode => Object.hash(runtimeType, accessControl, stack, vpnProps); @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$ThemeStateImplCopyWith<_$ThemeStateImpl> get copyWith => - __$$ThemeStateImplCopyWithImpl<_$ThemeStateImpl>(this, _$identity); + _$$VPNStateImplCopyWith<_$VPNStateImpl> get copyWith => + __$$VPNStateImplCopyWithImpl<_$VPNStateImpl>(this, _$identity); } -abstract class _ThemeState implements ThemeState { - const factory _ThemeState( - {required final String? locale, - required final ScaleProps scaleProps}) = _$ThemeStateImpl; +abstract class _VPNState implements VPNState { + const factory _VPNState( + {required final AccessControl? accessControl, + required final TunStack stack, + required final VpnProps vpnProps}) = _$VPNStateImpl; @override - String? get locale; + AccessControl? get accessControl; + @override + TunStack get stack; @override - ScaleProps get scaleProps; + VpnProps get vpnProps; @override @JsonKey(ignore: true) - _$$ThemeStateImplCopyWith<_$ThemeStateImpl> get copyWith => + _$$VPNStateImplCopyWith<_$VPNStateImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/models/selector.dart b/lib/models/selector.dart index 85be4822..5d7b7db2 100644 --- a/lib/models/selector.dart +++ b/lib/models/selector.dart @@ -63,6 +63,7 @@ class TrayState with _$TrayState { const factory TrayState({ required Mode mode, required bool autoLaunch, + required bool adminAutoLaunch, required bool systemProxy, required bool tunEnable, required bool isStart, @@ -89,7 +90,6 @@ class HomeState with _$HomeState { }) = _HomeState; } - @freezed class ProxiesCardSelectorState with _$ProxiesCardSelectorState { const factory ProxiesCardSelectorState({ @@ -196,7 +196,7 @@ class ProxiesActionsState with _$ProxiesActionsState { class AutoLaunchState with _$AutoLaunchState { const factory AutoLaunchState({ required bool isAutoLaunch, - required bool isOpenTun, + required bool isAdminAutoLaunch, }) = _AutoLaunchState; } @@ -217,6 +217,8 @@ class HttpOverridesState with _$HttpOverridesState { }) = _HttpOverridesState; } + + @freezed class ClashConfigState with _$ClashConfigState { const factory ClashConfigState({ @@ -242,10 +244,10 @@ class ClashConfigState with _$ClashConfigState { } @freezed -class ThemeState with _$ThemeState { - const factory ThemeState({ - required String? locale, - required ScaleProps scaleProps, - }) = _ThemeState; -} - +class VPNState with _$VPNState { + const factory VPNState({ + required AccessControl? accessControl, + required TunStack stack, + required VpnProps vpnProps, + }) = _VPNState; +} \ No newline at end of file diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 314cfb62..07858d0d 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -48,7 +48,7 @@ class HomePage extends StatelessWidget { child: SingleChildScrollView( child: IntrinsicHeight( child: Selector( - selector: (_, config) => config.showLabel, + selector: (_, config) => config.appSetting.showLabel, builder: (_, showLabel, __) { return NavigationRail( backgroundColor: @@ -96,7 +96,10 @@ class HomePage extends StatelessWidget { IconButton( onPressed: () { final config = globalState.appController.config; - config.showLabel = !config.showLabel; + final appSetting = config.appSetting; + config.appSetting = appSetting.copyWith( + showLabel: !appSetting.showLabel, + ); }, icon: const Icon(Icons.menu), ) @@ -160,7 +163,7 @@ class HomePage extends StatelessWidget { currentLabel: appState.currentLabel, navigationItems: appState.currentNavigationItems, viewMode: appState.viewMode, - locale: config.locale, + locale: config.appSetting.locale, ); }, shouldRebuild: (prev, next) { @@ -171,7 +174,7 @@ class HomePage extends StatelessWidget { final navigationItems = state.navigationItems; final currentLabel = state.currentLabel; final index = navigationItems.lastIndexWhere( - (element) => element.label == currentLabel, + (element) => element.label == currentLabel, ); final currentIndex = index == -1 ? 0 : index; final navigationBar = _getNavigationBar( @@ -181,9 +184,9 @@ class HomePage extends StatelessWidget { currentIndex: currentIndex, ); final bottomNavigationBar = - viewMode == ViewMode.mobile ? navigationBar : null; + viewMode == ViewMode.mobile ? navigationBar : null; final sideNavigationBar = - viewMode != ViewMode.mobile ? navigationBar : null; + viewMode != ViewMode.mobile ? navigationBar : null; return CommonScaffold( key: globalState.homeScaffoldKey, title: Intl.message( diff --git a/lib/plugins/vpn.dart b/lib/plugins/vpn.dart index 4003d391..327eb474 100644 --- a/lib/plugins/vpn.dart +++ b/lib/plugins/vpn.dart @@ -19,10 +19,8 @@ class Vpn { methodChannel.setMethodCallHandler((call) async { switch (call.method) { case "started": - final tunProps = call.arguments != null - ? TunProps.fromJson(json.decode((call.arguments))) - : null; - onStarted(tunProps); + final fd = call.arguments as int; + onStarted(fd); break; case "gc": clashCore.requestGc(); @@ -40,11 +38,10 @@ class Vpn { return _instance!; } - Future startVpn(port) async { - final state = clashCore.getState(); + Future startVpn() async { + final options = clashCore.getAndroidVpnOptions(); return await methodChannel.invokeMethod("start", { - 'port': state.mixedPort, - 'args': json.encode(state), + 'data': json.encode(options), }); } @@ -72,7 +69,7 @@ class Vpn { }); } - onStarted(TunProps? tunProps) { + onStarted(int fd) { if (receiver != null) { receiver!.close(); receiver == null; @@ -81,7 +78,7 @@ class Vpn { receiver!.listen((message) { _handleServiceMessage(message); }); - clashCore.startTun(tunProps, receiver!.sendPort.nativePort); + clashCore.startTun(fd, receiver!.sendPort.nativePort); } setServiceMessageHandler(ServiceMessageListener serviceMessageListener) { diff --git a/lib/state.dart b/lib/state.dart index 8052562f..87d69672 100644 --- a/lib/state.dart +++ b/lib/state.dart @@ -18,7 +18,6 @@ class GlobalState { Timer? timer; Timer? groupsUpdateTimer; var isVpnService = false; - var autoRun = false; late PackageInfo packageInfo; Function? updateCurrentDelayDebounce; PageController? pageController; @@ -60,7 +59,7 @@ class GlobalState { isCompatible: true, selectedMap: config.currentSelectedMap, overrideDns: config.overrideDns, - testUrl: config.testUrl, + testUrl: config.appSetting.testUrl, ), ), ); @@ -77,11 +76,13 @@ class GlobalState { }) async { clashCore.start(); if (globalState.isVpnService) { - await vpn?.startVpn(clashConfig.mixedPort); + await vpn?.startVpn(); startListenUpdate(); return; } startTime ??= DateTime.now(); + await preferences.saveClashConfig(clashConfig); + await preferences.saveConfig(config); await service?.init(); startListenUpdate(); } @@ -129,20 +130,20 @@ class GlobalState { config: config, clashConfig: clashConfig, ); - clashCore.setState( - CoreState( - enable: config.vpnProps.enable, - accessControl: config.isAccessControl ? config.accessControl : null, - ipv6: config.vpnProps.ipv6, - allowBypass: config.vpnProps.allowBypass, - systemProxy: config.vpnProps.systemProxy, - mixedPort: clashConfig.mixedPort, - onlyProxy: config.onlyProxy, - currentProfileName: - config.currentProfile?.label ?? config.currentProfileId ?? "", - ), - ); } + clashCore.setState( + CoreState( + enable: config.vpnProps.enable, + accessControl: config.isAccessControl ? config.accessControl : null, + ipv6: config.vpnProps.ipv6, + allowBypass: config.vpnProps.allowBypass, + systemProxy: config.vpnProps.systemProxy, + onlyProxy: config.appSetting.onlyProxy, + bypassDomain: config.vpnProps.bypassDomain, + currentProfileName: + config.currentProfile?.label ?? config.currentProfileId ?? "", + ), + ); updateCoreVersionInfo(appState); } @@ -202,7 +203,7 @@ class GlobalState { proxyName: proxyName, ), ); - if (config.isCloseConnections) { + if (config.appSetting.closeConnections) { clashCore.closeConnections(); } } @@ -228,7 +229,7 @@ class GlobalState { final traffic = clashCore.getTraffic(); if (Platform.isAndroid && isVpnService == true) { vpn?.startForeground( - title: clashCore.getState().currentProfileName, + title: clashCore.getCurrentProfileName(), content: "$traffic", ); } else { diff --git a/lib/widgets/builder.dart b/lib/widgets/builder.dart index 1234c029..703280c0 100644 --- a/lib/widgets/builder.dart +++ b/lib/widgets/builder.dart @@ -68,29 +68,6 @@ class ProxiesActionsBuilder extends StatelessWidget { typedef StateWidgetBuilder = Widget Function(T state); -class ScaleBuilder extends StatelessWidget { - final StateWidgetBuilder builder; - - const ScaleBuilder({ - super.key, - required this.builder, - }); - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, config) { - return config.scaleProps.custom - ? config.scaleProps.scale - : 1; - }, - builder: (_, state, __) { - return builder(state); - }, - ); - } -} - class LocaleBuilder extends StatelessWidget { final StateWidgetBuilder builder; @@ -102,7 +79,7 @@ class LocaleBuilder extends StatelessWidget { @override Widget build(BuildContext context) { return Selector( - selector: (_, config) => config.locale, + selector: (_, config) => config.appSetting.locale, builder: (_, state, __) { return builder(state); }, diff --git a/lib/widgets/icon.dart b/lib/widgets/icon.dart new file mode 100644 index 00000000..082b43e3 --- /dev/null +++ b/lib/widgets/icon.dart @@ -0,0 +1,52 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:fl_clash/common/common.dart'; +import 'package:flutter/material.dart'; + +class CommonIcon extends StatelessWidget { + final String src; + final double size; + + const CommonIcon({ + super.key, + required this.src, + required this.size, + }); + + Widget _defaultIcon() { + return Icon( + IconsExt.target, + size: size, + ); + } + + Widget _buildIcon() { + if (src.isEmpty) { + return _defaultIcon(); + } + final base64 = src.getBase64; + if (base64 != null) { + return Image.memory( + base64, + gaplessPlayback: true, + errorBuilder: (_, error, ___) { + return _defaultIcon(); + }, + ); + } + return CachedNetworkImage( + imageUrl: src, + fadeInDuration: Duration.zero, + fadeOutDuration: Duration.zero, + errorWidget: (_, __, ___) => _defaultIcon(), + ); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + width: size, + height: size, + child: _buildIcon(), + ); + } +} diff --git a/lib/widgets/input.dart b/lib/widgets/input.dart index 28cc30f4..0a619d57 100644 --- a/lib/widgets/input.dart +++ b/lib/widgets/input.dart @@ -1,8 +1,9 @@ +import 'package:fl_clash/common/app_localizations.dart'; import 'package:fl_clash/common/constant.dart'; +import 'package:fl_clash/models/common.dart'; import 'package:fl_clash/state.dart'; +import 'package:fl_clash/widgets/null_status.dart'; import 'package:flutter/material.dart'; - -import '../common/app_localizations.dart'; import 'card.dart'; import 'float_layout.dart'; import 'list.dart'; @@ -142,72 +143,144 @@ class _InputDialogState extends State { } } -class UpdatePage extends StatelessWidget { +class ListPage extends StatelessWidget { final String title; final Iterable items; + final Key Function(T item)? keyBuilder; final Widget Function(T item) titleBuilder; final Widget Function(T item)? subtitleBuilder; - final Function(T item) onAdd; - final Function(T item) onRemove; + final Widget Function(T item)? leadingBuilder; + final String? keyLabel; + final String? valueLabel; + final Function(Iterable items) onChange; - const UpdatePage({ + const ListPage({ super.key, required this.title, required this.items, + this.keyBuilder, required this.titleBuilder, - required this.onRemove, - required this.onAdd, + required this.onChange, + this.leadingBuilder, + this.keyLabel, + this.valueLabel, this.subtitleBuilder, }); bool get isMap => items is Iterable; - _handleEdit(T item) async { - if (isMap) { - item as MapEntry; - final value = await globalState.showCommonDialog( - child: AddDialog( - defaultKey: item.key, - defaultValue: item.value, - title: title, + _handleAddOrEdit([T? item]) async { + final value = await globalState.showCommonDialog( + child: AddDialog( + keyField: isMap + ? Field( + label: this.keyLabel ?? appLocalizations.key, + value: + item == null ? "" : (item as MapEntry).key, + ) + : null, + valueField: Field( + label: this.valueLabel ?? appLocalizations.value, + value: item == null + ? "" + : isMap + ? (item as MapEntry).value + : item as String, ), + title: title, + ), + ); + if (value == null) return; + final entries = List.from( + items, + ); + if (item != null) { + final index = entries.indexWhere( + (entry) { + if (isMap) { + return (entry as MapEntry).key == + (item as MapEntry).key; + } + return entry == item; + }, ); - if (value == null) return; - onAdd(value); + if (index != -1) { + entries[index] = value; + } } else { - item as String; - final value = await globalState.showCommonDialog( - child: AddDialog( - defaultKey: null, - defaultValue: item, - title: title, - ), - ); - if (value == null) return; - onAdd(value); + entries.add(value); } + onChange(entries); } - @override - Widget build(BuildContext context) { - return FloatLayout( - floatingWidget: FloatWrapper( - child: FloatingActionButton( - onPressed: () async { - final value = await globalState.showCommonDialog( - child: AddDialog( - defaultKey: isMap ? "" : null, - defaultValue: "", - title: title, - ), - ); - if (value == null) return; - onAdd(value); - }, - child: const Icon(Icons.add), + _handleDelete(T item) { + final entries = List.from( + items, + ); + final index = entries.indexWhere( + (entry) { + if (isMap) { + return (entry as MapEntry).key == + (item as MapEntry).key; + } + return entry == item; + }, + ); + if (index != -1) { + entries.removeAt(index); + } + onChange(entries); + } + + Widget _buildList() { + final items = this.items.toList(); + if (this.keyBuilder != null) { + return ReorderableListView.builder( + padding: const EdgeInsets.only( + bottom: 16 + 64, + left: 16, + right: 16, ), - ), - child: ListView.builder( + buildDefaultDragHandles: false, + itemCount: items.length, + itemBuilder: (_, index) { + final e = items[index]; + return Padding( + key: keyBuilder!(e), + padding: const EdgeInsets.symmetric(vertical: 8), + child: ReorderableDragStartListener( + index: index, + child: CommonCard( + child: ListItem( + leading: leadingBuilder != null ? leadingBuilder!(e) : null, + title: titleBuilder(e), + subtitle: subtitleBuilder != null ? subtitleBuilder!(e) : null, + trailing: IconButton( + icon: const Icon(Icons.delete_outline), + onPressed: () { + _handleDelete(e); + }, + ), + ), + onPressed: () { + _handleAddOrEdit(e); + }, + ), + ), + ); + }, + onReorder: (oldIndex, newIndex) { + if (oldIndex < newIndex) { + newIndex -= 1; + } + final nextItems = List.from(items); + final item = nextItems.removeAt(oldIndex); + nextItems.insert(newIndex, item); + onChange(nextItems); + }, + ); + } else { + return ListView.builder( padding: const EdgeInsets.only( bottom: 16 + 64, left: 16, @@ -215,41 +288,60 @@ class UpdatePage extends StatelessWidget { ), itemCount: items.length, itemBuilder: (_, index) { - final e = items.toList()[index]; + final e = items[index]; return Padding( + key: ObjectKey(e.toString()), padding: const EdgeInsets.symmetric(vertical: 8), child: CommonCard( child: ListItem( + leading: leadingBuilder != null ? leadingBuilder!(e) : null, title: titleBuilder(e), subtitle: subtitleBuilder != null ? subtitleBuilder!(e) : null, trailing: IconButton( icon: const Icon(Icons.delete_outline), onPressed: () { - onRemove(e); + _handleDelete(e); }, ), ), onPressed: () { - _handleEdit(e); + _handleAddOrEdit(e); }, ), ); }, + ); + } + } + + @override + Widget build(BuildContext context) { + return FloatLayout( + floatingWidget: FloatWrapper( + child: FloatingActionButton( + onPressed: () async { + _handleAddOrEdit(); + }, + child: const Icon(Icons.add), + ), ), + child: items.isEmpty + ? NullStatus(label: appLocalizations.noData) + : _buildList(), ); } } class AddDialog extends StatefulWidget { final String title; - final String? defaultKey; - final String defaultValue; + final Field? keyField; + final Field valueField; const AddDialog({ super.key, required this.title, - this.defaultKey, - required this.defaultValue, + this.keyField, + required this.valueField, }); @override @@ -257,29 +349,33 @@ class AddDialog extends StatefulWidget { } class _AddDialogState extends State { - late TextEditingController keyController; + TextEditingController? keyController; late TextEditingController valueController; final GlobalKey _formKey = GlobalKey(); + Field? get keyField => widget.keyField; + + Field get valueField => widget.valueField; + @override void initState() { super.initState(); - keyController = TextEditingController( - text: widget.defaultKey, - ); + if (keyField != null) { + keyController = TextEditingController( + text: keyField!.value, + ); + } valueController = TextEditingController( - text: widget.defaultValue, + text: valueField.value, ); } - bool get hasKey => widget.defaultKey != null; - _submit() { if (!_formKey.currentState!.validate()) return; - if (hasKey) { + if (keyField != null) { Navigator.of(context).pop>( MapEntry( - keyController.text, + keyController!.text, valueController.text, ), ); @@ -301,19 +397,21 @@ class _AddDialogState extends State { child: Wrap( runSpacing: 16, children: [ - if (hasKey) + if (keyField != null) TextFormField( maxLines: 2, minLines: 1, controller: keyController, decoration: InputDecoration( - prefixIcon: const Icon(Icons.key), border: const OutlineInputBorder(), - labelText: appLocalizations.key, + labelText: keyField!.label, ), validator: (String? value) { + if (keyField!.validator != null) { + return keyField!.validator!(value); + } if (value == null || value.isEmpty) { - return appLocalizations.keyNotEmpty; + return appLocalizations.notEmpty; } return null; }, @@ -323,13 +421,15 @@ class _AddDialogState extends State { minLines: 1, controller: valueController, decoration: InputDecoration( - prefixIcon: const Icon(Icons.label), border: const OutlineInputBorder(), - labelText: appLocalizations.value, + labelText: valueField.label, ), validator: (String? value) { + if (valueField.validator != null) { + return valueField.validator!(value); + } if (value == null || value.isEmpty) { - return appLocalizations.valueNotEmpty; + return appLocalizations.notEmpty; } return null; }, diff --git a/lib/widgets/scaffold.dart b/lib/widgets/scaffold.dart index 86e0e657..2bdaca33 100644 --- a/lib/widgets/scaffold.dart +++ b/lib/widgets/scaffold.dart @@ -1,6 +1,7 @@ import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; class CommonScaffold extends StatefulWidget { final Widget body; @@ -112,6 +113,21 @@ class CommonScaffoldState extends State { actions.isNotEmpty ? actions : widget.actions; return AppBar( centerTitle: false, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarIconBrightness: + Theme.of(context).brightness == Brightness.dark + ? Brightness.light + : Brightness.dark, + systemNavigationBarIconBrightness: + Theme.of(context).brightness == Brightness.dark + ? Brightness.light + : Brightness.dark, + systemNavigationBarColor: widget.bottomNavigationBar != null + ? context.colorScheme.surfaceContainer + : context.colorScheme.surface, + systemNavigationBarDividerColor: Colors.transparent, + ), automaticallyImplyLeading: widget.automaticallyImplyLeading, leading: widget.leading, title: Text(widget.title), diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index 094dab2f..1b735b1b 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -21,3 +21,4 @@ export 'setting.dart'; export 'input.dart'; export 'keep_scope.dart'; export 'back_scope.dart'; +export 'icon.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 466f928b..52931e94 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,9 @@ 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.62+202409261 +version: 0.8.63+202410081 environment: sdk: '>=3.1.0 <4.0.0' - flutter: 3.22.0 dependencies: flutter: