Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
d9d22d7616 Add android separates the core process
Support core status check and force restart

Update flutter and pub dependencies
2025-08-29 13:26:12 +08:00
67 changed files with 581 additions and 435 deletions

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<permission
android:name="${applicationId}.permission.RECEIVE_BROADCASTS"
android:protectionLevel="signature" />
@@ -112,7 +112,9 @@
android:exported="true"
android:permission="${applicationId}.permission.RECEIVE_BROADCASTS">
<intent-filter>
<action android:name="${applicationId}.action.CREATE_VPN" />
<action android:name="${applicationId}.intent.action.START" />
<action android:name="${applicationId}.intent.action.STOP" />
<action android:name="${applicationId}.intent.action.TOGGLE" />
</intent-filter>
</receiver>

View File

@@ -14,11 +14,21 @@ class BroadcastReceiver : BroadcastReceiver(),
CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
BroadcastAction.CREATE_VPN.action -> {
BroadcastAction.START.action -> {
launch {
State.handleStartServiceAction()
}
}
BroadcastAction.STOP.action -> {
State.handleStopServiceAction()
}
BroadcastAction.TOGGLE.action -> {
launch {
State.handleToggleAction()
}
}
}
}
}

View File

@@ -27,7 +27,7 @@ suspend fun Drawable.getBase64(): String {
suspend fun <T> MethodChannel.awaitResult(
method: String, arguments: Any? = null
): T? = withContext(Dispatchers.Main) { // 切换到主线程
): T? = withContext(Dispatchers.Main) {
suspendCancellableCoroutine { continuation ->
invokeMethod(method, arguments, object : MethodChannel.Result {
override fun success(result: Any?) {

View File

@@ -1,19 +1,23 @@
package com.follow.clash
import android.os.Bundle
import android.os.PersistableBundle
import com.follow.clash.common.GlobalState
import androidx.lifecycle.lifecycleScope
import com.follow.clash.plugins.AppPlugin
import com.follow.clash.plugins.ServicePlugin
import com.follow.clash.plugins.TilePlugin
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
class MainActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
GlobalState.launch {
class MainActivity : FlutterActivity(),
CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
State.destroyServiceEngine()
}
}

View File

@@ -3,6 +3,7 @@ package com.follow.clash
import com.follow.clash.common.ServiceDelegate
import com.follow.clash.common.intent
import com.follow.clash.service.ICallbackInterface
import com.follow.clash.service.IMessageInterface
import com.follow.clash.service.IRemoteInterface
import com.follow.clash.service.RemoteService
import com.follow.clash.service.models.NotificationParams
@@ -36,12 +37,12 @@ object Service {
}
suspend fun invokeAction(
data: String, cb: (result: String?) -> Unit
data: String, cb: (result: ByteArray?, isSuccess: Boolean) -> Unit
) {
delegate.useService {
it.invokeAction(data, object : ICallbackInterface.Stub() {
override fun onResult(result: String?) {
cb(result)
override fun onResult(result: ByteArray?, isSuccess: Boolean) {
cb(result, isSuccess)
}
})
}
@@ -59,7 +60,7 @@ object Service {
cb: (result: String?) -> Unit
) {
delegate.useService {
it.setMessageCallback(object : ICallbackInterface.Stub() {
it.setMessageCallback(object : IMessageInterface.Stub() {
override fun onResult(result: String?) {
cb(result)
}

View File

@@ -53,12 +53,18 @@ object State {
suspend fun handleStartServiceAction() {
tilePlugin?.handleStart()
if (flutterEngine != null) {
return
}
startServiceWithEngine()
}
suspend fun handleStopServiceAction() {
fun handleStopServiceAction() {
tilePlugin?.handleStop()
destroyServiceEngine()
if (flutterEngine != null || serviceFlutterEngine != null) {
return
}
handleStopService()
}
fun handleStartService() {
@@ -73,15 +79,16 @@ object State {
suspend fun destroyServiceEngine() {
runLock.withLock {
serviceFlutterEngine?.destroy()
serviceFlutterEngine = null
withContext(Dispatchers.Main) {
runCatching {
serviceFlutterEngine?.destroy()
serviceFlutterEngine = null
}
}
}
}
suspend fun startServiceWithEngine() {
if (flutterEngine != null) {
return
}
runLock.withLock {
withContext(Dispatchers.Main) {
serviceFlutterEngine = FlutterEngine(GlobalState.application)
@@ -100,6 +107,9 @@ object State {
private fun startService() {
GlobalState.launch {
runLock.withLock {
if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.START) {
return@launch
}
runStateFlow.tryEmit(RunState.PENDING)
if (servicePlugin == null) {
return@launch
@@ -109,7 +119,7 @@ object State {
return@launch
}
appPlugin?.prepare(options.enable) {
servicePlugin?.startService(options, true)
Service.startService(options, true)
runStateFlow.tryEmit(RunState.START)
runTime = System.currentTimeMillis()
}
@@ -121,11 +131,15 @@ object State {
fun handleStopService() {
GlobalState.launch {
runLock.withLock {
if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.STOP) {
return@launch
}
runStateFlow.tryEmit(RunState.PENDING)
servicePlugin?.stopService()
Service.stopService()
runStateFlow.tryEmit(RunState.STOP)
runTime = 0
}
destroyServiceEngine()
}
}

View File

@@ -21,9 +21,7 @@ class TempActivity : Activity(),
}
QuickAction.STOP.action -> {
launch {
State.handleStopServiceAction()
}
State.handleStopServiceAction()
}
QuickAction.TOGGLE.action -> {

View File

@@ -285,9 +285,12 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
NOTIFICATION_PERMISSION_REQUEST_CODE
)
return
}
return
} else {
invokeRequestNotificationCallback()
}
}
fun invokeRequestNotificationCallback() {

View File

@@ -1,9 +1,11 @@
package com.follow.clash.plugins
import com.follow.clash.RunState
import com.follow.clash.Service
import com.follow.clash.State
import com.follow.clash.awaitResult
import com.follow.clash.common.Components
import com.follow.clash.common.formatString
import com.follow.clash.invokeMethodOnMainThread
import com.follow.clash.models.AppState
import com.follow.clash.service.models.NotificationParams
@@ -67,8 +69,12 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
private fun handleInvokeAction(call: MethodCall, result: MethodChannel.Result) {
launch {
val data = call.arguments<String>()!!
Service.invokeAction(data) {
result.success(it)
val res = mutableListOf<ByteArray>()
Service.invokeAction(data) { byteArray, isSuccess ->
res.add(byteArray ?: byteArrayOf())
if (isSuccess) {
result.success(res.formatString())
}
}
}
}
@@ -88,14 +94,6 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
return Gson().fromJson(res, VpnOptions::class.java)
}
suspend fun startService(options: VpnOptions, inApp: Boolean) {
Service.startService(options, inApp)
}
suspend fun stopService() {
Service.stopService()
}
val semaphore = Semaphore(10)
fun handleSendEvent(value: String?) {
@@ -107,6 +105,7 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
}
private fun onServiceCrash() {
State.runStateFlow.tryEmit(RunState.STOP)
flutterMethodChannel.invokeMethodOnMainThread<Any>("crash", null)
}
@@ -123,12 +122,11 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
)
result.success(true)
}
}
fun handleInit(result: MethodChannel.Result) {
Service.bind()
launch {
Service.bind()
Service.setMessageCallback {
handleSendEvent(it)
}

View File

@@ -10,4 +10,7 @@ object Components {
val TEMP_ACTIVITY =
ComponentName(GlobalState.packageName, "${PACKAGE_NAME}.TempActivity")
val BROADCAST_RECEIVER =
ComponentName(GlobalState.packageName, "${PACKAGE_NAME}.BroadcastReceiver")
}

View File

@@ -10,7 +10,9 @@ enum class QuickAction {
}
enum class BroadcastAction {
CREATE_VPN,
START,
STOP,
TOGGLE,
}
enum class AccessControlMode {

View File

@@ -21,6 +21,7 @@ import android.util.Log
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import java.nio.charset.Charset
import kotlin.reflect.KClass
//fun Context.startForegroundServiceCompat(intent: Intent?) {
@@ -42,18 +43,27 @@ fun Service.startForegroundCompat(id: Int, notification: Notification) {
}
}
val Enum<*>.action: String
val QuickAction.action: String
get() = "${GlobalState.application.packageName}.action.${this.name}"
val QuickAction.quickIntent: Intent
get() = Intent().apply {
Log.d("[quickIntent]", Components.TEMP_ACTIVITY.toString())
setComponent(Components.TEMP_ACTIVITY)
setPackage(GlobalState.packageName)
action = this@quickIntent.action
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
}
val BroadcastAction.action: String
get() = "${GlobalState.application.packageName}.intent.action.${this.name}"
val BroadcastAction.quickIntent: Intent
get() = Intent().apply {
setComponent(Components.BROADCAST_RECEIVER)
setPackage(GlobalState.packageName)
action = this@quickIntent.action
}
fun BroadcastAction.sendBroadcast() {
val intent = Intent().apply {
action = this@sendBroadcast.action
@@ -192,4 +202,37 @@ val Long.formatBytes: String
} else {
"%.1f${units[unitIndex]}".format(size)
}
}
}
fun String.chunkedForAidl(charset: Charset = Charsets.UTF_8): List<ByteArray> {
val allBytes = toByteArray(charset)
val total = allBytes.size
val maxBytes = when {
total <= 100 * 1024 -> total
total <= 1024 * 1024 -> 64 * 1024
total <= 10 * 1024 * 1024 -> 128 * 1024
else -> 256 * 1024
}
val result = mutableListOf<ByteArray>()
var index = 0
while (index < total) {
val end = minOf(index + maxBytes, total)
result.add(allBytes.copyOfRange(index, end))
index = end
}
return result
}
fun <T : List<ByteArray>> T.formatString(charset: Charset = Charsets.UTF_8): String {
val totalSize = this.sumOf { it.size }
val combined = ByteArray(totalSize)
var offset = 0
this.forEach { byteArray ->
byteArray.copyInto(combined, offset)
offset += byteArray.size
}
return String(combined, charset)
}

View File

@@ -6,14 +6,17 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.retryWhen
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
class ServiceDelegate<T>(
private val intent: Intent,
private val onServiceDisconnected: (() -> Unit)? = null,
private val onServiceCrash: (() -> Unit)? = null,
private val interfaceCreator: (IBinder) -> T,
) : CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) {
@@ -31,6 +34,7 @@ class ServiceDelegate<T>(
is BindServiceEvent.Disconnected -> {
_service.value = null
onServiceDisconnected?.invoke()
}
is BindServiceEvent.Crashed -> {
@@ -49,16 +53,21 @@ class ServiceDelegate<T>(
}
}
suspend inline fun <R> useService(crossinline block: (T) -> R): Result<R> {
return withTimeoutOrNull(10_000) {
service.first { it != null }
}?.let { service ->
try {
Result.success(block(service))
} catch (e: Exception) {
Result.failure(e)
}
} ?: Result.failure(Exception("Service connection timeout"))
suspend inline fun <R> useService(
retries: Int = 10,
delayMillis: Long = 200,
crossinline block: (T) -> R
): Result<R> {
return runCatching {
service.filterNotNull()
.retryWhen { _, attempt ->
(attempt < retries).also {
if (it) delay(delayMillis)
}
}.first().let {
block(it)
}
}
}
fun unbind() {

View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@@ -9,14 +9,17 @@
extern "C"
JNIEXPORT void JNICALL
Java_com_follow_clash_core_Core_startTun(JNIEnv *env, jobject thiz, jint fd, jobject cb,
jstring address, jstring dns) {
jstring stack, jstring address, jstring dns) {
const auto interface = new_global(cb);
startTUN(interface, fd, get_string(address), get_string(dns));
scoped_string stackChar = get_string(stack);
scoped_string addressChar = get_string(address);
scoped_string dnsChar = get_string(dns);
startTUN(interface, fd, stackChar, addressChar, dnsChar);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_follow_clash_core_Core_stopTun(JNIEnv *) {
Java_com_follow_clash_core_Core_stopTun(JNIEnv *env, jobject thiz) {
stopTun();
}
@@ -29,14 +32,16 @@ Java_com_follow_clash_core_Core_forceGC(JNIEnv *env, jobject thiz) {
extern "C"
JNIEXPORT void JNICALL
Java_com_follow_clash_core_Core_updateDNS(JNIEnv *env, jobject thiz, jstring dns) {
updateDns(get_string(dns));
scoped_string dnsChar = get_string(dns);
updateDns(dnsChar);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_follow_clash_core_Core_invokeAction(JNIEnv *env, jobject thiz, jstring data, jobject cb) {
const auto interface = new_global(cb);
invokeAction(interface, get_string(data));
scoped_string dataChar = get_string(data);
invokeAction(interface, dataChar);
}
extern "C"
@@ -99,7 +104,8 @@ call_tun_interface_resolve_process_impl(void *tun_interface, const int protocol,
new_string(source),
new_string(target),
uid));
return get_string(packageName);
scoped_string packageNameChar = get_string(packageName);
return packageNameChar;
}
static void call_invoke_interface_result_impl(void *invoke_interface, const char *data) {
@@ -141,7 +147,7 @@ JNI_OnLoad(JavaVM *vm, void *) {
extern "C"
JNIEXPORT void JNICALL
Java_com_follow_clash_core_Core_startTun(JNIEnv *env, jobject thiz, jint fd, jobject cb,
jstring address, jstring dns) {
jstring stack, jstring address, jstring dns) {
}
extern "C"
@@ -184,4 +190,4 @@ extern "C"
JNIEXPORT void JNICALL
Java_com_follow_clash_core_Core_suspended(JNIEnv *env, jobject thiz, jboolean suspended) {
}
#endif
#endif

View File

@@ -8,6 +8,7 @@ data object Core {
private external fun startTun(
fd: Int,
cb: TunInterface,
stack: String,
address: String,
dns: String,
)
@@ -29,6 +30,7 @@ data object Core {
fd: Int,
protect: (Int) -> Boolean,
resolverProcess: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress, uid: Int) -> String,
stack: String,
address: String,
dns: String,
) {
@@ -53,6 +55,7 @@ data object Core {
)
}
},
stack,
address,
dns
)

View File

@@ -2,5 +2,5 @@
package com.follow.clash.service;
interface ICallbackInterface {
void onResult(String result);
void onResult(in byte[] result, boolean isSuccess);
}

View File

@@ -0,0 +1,6 @@
// IMessageInterface.aidl
package com.follow.clash.service;
interface IMessageInterface {
void onResult(String result);
}

View File

@@ -2,6 +2,7 @@
package com.follow.clash.service;
import com.follow.clash.service.ICallbackInterface;
import com.follow.clash.service.IMessageInterface;
import com.follow.clash.service.models.VpnOptions;
import com.follow.clash.service.models.NotificationParams;
@@ -10,5 +11,5 @@ interface IRemoteInterface {
void updateNotificationParams(in NotificationParams params);
void startService(in VpnOptions options,in boolean inApp);
void stopService();
void setMessageCallback(in ICallbackInterface messageCallback);
void setMessageCallback(in IMessageInterface messageCallback);
}

View File

@@ -6,7 +6,7 @@ import com.follow.clash.common.sendBroadcast
interface IBaseService {
fun handleCreate() {
if (!State.inApp) {
BroadcastAction.CREATE_VPN.sendBroadcast()
BroadcastAction.START.sendBroadcast()
} else {
State.inApp = false
}

View File

@@ -4,6 +4,7 @@ import android.app.Service
import android.content.Intent
import android.os.IBinder
import com.follow.clash.common.ServiceDelegate
import com.follow.clash.common.chunkedForAidl
import com.follow.clash.common.intent
import com.follow.clash.core.Core
import com.follow.clash.service.models.NotificationParams
@@ -22,11 +23,15 @@ class RemoteService : Service(),
launch {
delegate?.useService { service ->
service.stop()
delegate?.unbind()
}
delegate?.unbind()
}
}
fun onServiceDisconnected() {
handleStopService()
}
private fun handleStartService() {
launch {
val nextIntent = when (State.options?.enable == true) {
@@ -35,7 +40,7 @@ class RemoteService : Service(),
}
if (intent != nextIntent) {
delegate?.unbind()
delegate = ServiceDelegate(nextIntent, {}) { binder ->
delegate = ServiceDelegate(nextIntent, ::onServiceDisconnected) { binder ->
when (binder) {
is VpnService.LocalBinder -> binder.getService()
is CommonService.LocalBinder -> binder.getService()
@@ -53,7 +58,13 @@ class RemoteService : Service(),
private val binder: IRemoteInterface.Stub = object : IRemoteInterface.Stub() {
override fun invokeAction(data: String, callback: ICallbackInterface) {
Core.invokeAction(data, callback::onResult)
Core.invokeAction(data) {
val chunks = it?.chunkedForAidl() ?: listOf()
val totalSize = chunks.size
chunks.forEachIndexed { index, chunk ->
callback.onResult(chunk, totalSize - 1 == index)
}
}
}
override fun updateNotificationParams(params: NotificationParams?) {
@@ -72,7 +83,7 @@ class RemoteService : Service(),
handleStopService()
}
override fun setMessageCallback(messageCallback: ICallbackInterface) {
override fun setMessageCallback(messageCallback: IMessageInterface) {
setMessageCallback(messageCallback::onResult)
}
}

View File

@@ -11,9 +11,8 @@ import android.os.RemoteException
import android.util.Log
import androidx.core.content.getSystemService
import com.follow.clash.common.AccessControlMode
import com.follow.clash.common.QuickAction
import com.follow.clash.common.quickIntent
import com.follow.clash.common.toPendingIntent
import com.follow.clash.common.BroadcastAction
import com.follow.clash.common.sendBroadcast
import com.follow.clash.core.Core
import com.follow.clash.service.models.VpnOptions
import com.follow.clash.service.models.getIpv4RouteAddress
@@ -44,6 +43,7 @@ class VpnService : SystemVpnService(), IBaseService,
super.onCreate()
handleCreate()
}
private val connectivity by lazy {
getSystemService<ConnectivityManager>()
}
@@ -107,7 +107,7 @@ class VpnService : SystemVpnService(), IBaseService,
try {
val isSuccess = super.onTransact(code, data, reply, flags)
if (!isSuccess) {
QuickAction.STOP.quickIntent.toPendingIntent.send()
BroadcastAction.STOP.sendBroadcast()
}
return isSuccess
} catch (e: RemoteException) {
@@ -220,6 +220,7 @@ class VpnService : SystemVpnService(), IBaseService,
fd,
protect = this::protect,
resolverProcess = this::resolverProcess,
options.stack,
options.address,
options.dns
)

View File

@@ -23,6 +23,7 @@ data class VpnOptions(
val allowBypass: Boolean,
val systemProxy: Boolean,
val bypassDomain: List<String>,
val stack: String,
val routeAddress: List<String>,
) : Parcelable

View File

@@ -28,6 +28,7 @@ class NetworkObserveModule(private val service: Service) : Module() {
private val connectivity by lazy {
service.getSystemService<ConnectivityManager>()
}
private var preDnsList = listOf<String>()
private val request = NetworkRequest.Builder().apply {
addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
@@ -61,6 +62,7 @@ class NetworkObserveModule(private val service: Service) : Module() {
override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
networkInfos[network]?.dnsList = linkProperties.dnsServers
onUpdateNetwork()
setUnderlyingNetworks(network)
super.onLinkPropertiesChanged(network, linkProperties)
}
@@ -96,6 +98,10 @@ class NetworkObserveModule(private val service: Service) : Module() {
fun onUpdateNetwork() {
val dnsList = (networkInfos.asSequence().minByOrNull { networkToInt(it) }?.value?.dnsList
?: emptyList()).map { x -> x.asSocketAddressText(53) }
if (dnsList == preDnsList) {
return
}
preDnsList = dnsList
Core.updateDNS(dnsList.joinToString { "," })
}

View File

@@ -25,18 +25,30 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.zip
import kotlinx.coroutines.launch
data class ExtendedNotificationParams(
val title: String,
val stopText: String,
val onlyStatisticsProxy: Boolean,
val contentText: String,
)
val NotificationParams.extended: ExtendedNotificationParams
get() = ExtendedNotificationParams(
title, stopText, onlyStatisticsProxy, Core.getSpeedTrafficText(onlyStatisticsProxy)
)
class NotificationModule(private val service: Service) : Module() {
private val scope = CoroutineScope(Dispatchers.Default)
override fun onInstall() {
State.notificationParamsFlow.value?.let {
update(it)
update(it.extended)
}
scope.launch {
val screenFlow = service.receiveBroadcastFlow {
@@ -48,11 +60,12 @@ class NotificationModule(private val service: Service) : Module() {
emit(isScreenOn())
}
tickerFlow(1000, 0)
.combine(State.notificationParamsFlow.zip(screenFlow) { params, screenOn ->
params to screenOn
}) { _, (params, screenOn) -> params to screenOn }
.filter { (params, screenOn) -> params != null && screenOn }
combine(
tickerFlow(1000, 0), State.notificationParamsFlow, screenFlow
) { _, params, screenOn ->
params?.extended to screenOn
}.filter { (params, screenOn) -> params != null && screenOn }
.distinctUntilChanged { old, new -> old.first == new.first && old.second == new.second }
.collect { (params, _) ->
update(params!!)
}
@@ -87,17 +100,15 @@ class NotificationModule(private val service: Service) : Module() {
}
}
private fun update(params: NotificationParams) {
val contentText = Core.getSpeedTrafficText(params.onlyStatisticsProxy)
private fun update(params: ExtendedNotificationParams) {
service.startForeground(
with(notificationBuilder) {
setContentTitle(params.title)
setContentText(contentText)
setContentText(params.contentText)
setPriority(NotificationCompat.PRIORITY_HIGH)
clearActions()
addAction(
0,
params.stopText,
QuickAction.STOP.quickIntent.toPendingIntent
0, params.stopText, QuickAction.STOP.quickIntent.toPendingIntent
).build()
})
}

View File

@@ -426,5 +426,7 @@
"disconnected": "Disconnected",
"connecting": "Connecting...",
"restartCoreTip": "Are you sure you want to restart the core?",
"forceRestartCoreTip": "Are you sure you want to force restart the core?"
"forceRestartCoreTip": "Are you sure you want to force restart the core?",
"dnsHijacking": "DNS hijacking",
"coreStatus": "Core status"
}

View File

@@ -427,5 +427,7 @@
"disconnected": "切断済み",
"connecting": "接続中...",
"restartCoreTip": "コアを再起動してもよろしいですか?",
"forceRestartCoreTip": "コアを強制再起動してもよろしいですか?"
"forceRestartCoreTip": "コアを強制再起動してもよろしいですか?",
"dnsHijacking": "DNSハイジャッキング",
"coreStatus": "コアステータス"
}

View File

@@ -427,5 +427,7 @@
"disconnected": "Отключено",
"connecting": "Подключение...",
"restartCoreTip": "Вы уверены, что хотите перезапустить ядро?",
"forceRestartCoreTip": "Вы уверены, что хотите принудительно перезапустить ядро?"
"forceRestartCoreTip": "Вы уверены, что хотите принудительно перезапустить ядро?",
"dnsHijacking": "DNS-перехват",
"coreStatus": "Основной статус"
}

View File

@@ -427,5 +427,7 @@
"disconnected": "已断开",
"connecting": "连接中...",
"restartCoreTip": "您确定要重启核心吗?",
"forceRestartCoreTip": "您确定要强制重启核心吗?"
"forceRestartCoreTip": "您确定要强制重启核心吗?",
"dnsHijacking": "DNS劫持",
"coreStatus": "核心状态"
}

View File

@@ -16,8 +16,7 @@ func resolveProcess(callback unsafe.Pointer, protocol int, source, target string
t := C.CString(target)
defer C.free(unsafe.Pointer(t))
res := C.resolve_process(callback, C.int(protocol), s, t, C.int(uid))
defer releaseObject(unsafe.Pointer(res))
return takeCString(res)
return parseCString(res)
}
func invokeResult(callback unsafe.Pointer, data string) {
@@ -30,7 +29,7 @@ func releaseObject(callback unsafe.Pointer) {
C.release_object(callback)
}
func takeCString(s *C.char) string {
defer releaseObject(unsafe.Pointer(s))
func parseCString(s *C.char) string {
//defer C.free(unsafe.Pointer(s))
return C.GoString(s)
}

View File

@@ -17,12 +17,14 @@ import (
"github.com/metacubex/mihomo/constant/features"
cp "github.com/metacubex/mihomo/constant/provider"
"github.com/metacubex/mihomo/hub"
"github.com/metacubex/mihomo/hub/executor"
"github.com/metacubex/mihomo/hub/route"
"github.com/metacubex/mihomo/listener"
"github.com/metacubex/mihomo/log"
rp "github.com/metacubex/mihomo/rules/provider"
"github.com/metacubex/mihomo/tunnel"
"os"
"path/filepath"
"sync"
)
@@ -159,7 +161,6 @@ func patchSelectGroup(mapping map[string]string) {
func defaultSetupParams() *SetupParams {
return &SetupParams{
Config: config.DefaultRawConfig(),
TestURL: "https://www.gstatic.com/generate_204",
SelectedMap: map[string]string{},
}
@@ -240,7 +241,7 @@ func setupConfig(params *SetupParams) error {
defer runLock.Unlock()
var err error
constant.DefaultTestURL = params.TestURL
currentConfig, err = config.ParseRawConfig(params.Config)
currentConfig, err = executor.ParseWithPath(filepath.Join(constant.Path.HomeDir(), "config.yaml"))
if err != nil {
currentConfig, _ = config.ParseRawConfig(config.DefaultRawConfig())
}

View File

@@ -4,7 +4,6 @@ import (
"encoding/json"
"github.com/metacubex/mihomo/adapter/provider"
P "github.com/metacubex/mihomo/component/process"
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/tunnel"
@@ -18,7 +17,6 @@ type InitParams struct {
}
type SetupParams struct {
Config *config.RawConfig `json:"config"`
SelectedMap map[string]string `json:"selected-map"`
TestURL string `json:"test-url"`
}

View File

@@ -36,11 +36,11 @@ type TunHandler struct {
limit *semaphore.Weighted
}
func (th *TunHandler) start(fd int, address, dns string) {
func (th *TunHandler) start(fd int, stack, address, dns string) {
_ = th.limit.Acquire(context.TODO(), 4)
defer th.limit.Release(4)
th.initHook()
tunListener := t.Start(fd, currentConfig.General.Tun.Device, currentConfig.General.Tun.Stack, address, dns)
tunListener := t.Start(fd, stack, address, dns)
if tunListener != nil {
log.Infoln("TUN address: %v", tunListener.Address())
th.listener = tunListener
@@ -136,7 +136,7 @@ func handleStopTun() {
}
}
func handleStartTun(callback unsafe.Pointer, fd int, address, dns string) {
func handleStartTun(callback unsafe.Pointer, fd int, stack, address, dns string) {
handleStopTun()
tunLock.Lock()
defer tunLock.Unlock()
@@ -145,7 +145,7 @@ func handleStartTun(callback unsafe.Pointer, fd int, address, dns string) {
callback: callback,
limit: semaphore.NewWeighted(4),
}
tunHandler.start(fd, address, dns)
tunHandler.start(fd, stack, address, dns)
}
}
@@ -166,7 +166,6 @@ func (result ActionResult) send() {
if result.Method != messageMethod {
releaseObject(result.callback)
}
}
func nextHandle(action *Action, result ActionResult) bool {
@@ -182,7 +181,7 @@ func nextHandle(action *Action, result ActionResult) bool {
//export invokeAction
func invokeAction(callback unsafe.Pointer, paramsChar *C.char) {
params := takeCString(paramsChar)
params := parseCString(paramsChar)
var action = &Action{}
err := json.Unmarshal([]byte(params), action)
if err != nil {
@@ -198,8 +197,8 @@ func invokeAction(callback unsafe.Pointer, paramsChar *C.char) {
}
//export startTUN
func startTUN(callback unsafe.Pointer, fd C.int, addressChar, dnsChar *C.char) bool {
handleStartTun(callback, int(fd), takeCString(addressChar), takeCString(dnsChar))
func startTUN(callback unsafe.Pointer, fd C.int, stackChar, addressChar, dnsChar *C.char) bool {
handleStartTun(callback, int(fd), parseCString(stackChar), parseCString(addressChar), parseCString(dnsChar))
return true
}
@@ -250,5 +249,5 @@ func forceGC() {
//export updateDns
func updateDns(s *C.char) {
handleUpdateDns(takeCString(s))
handleUpdateDns(parseCString(s))
}

View File

@@ -14,9 +14,13 @@ import (
"strings"
)
func Start(fd int, device string, stack constant.TUNStack, address, dns string) *sing_tun.Listener {
func Start(fd int, stack string, address, dns string) *sing_tun.Listener {
var prefix4 []netip.Prefix
var prefix6 []netip.Prefix
tunStack, ok := constant.StackTypeMapping[strings.ToLower(stack)]
if !ok {
tunStack = constant.TunSystem
}
for _, a := range strings.Split(address, ",") {
a = strings.TrimSpace(a)
if len(a) == 0 {
@@ -45,8 +49,8 @@ func Start(fd int, device string, stack constant.TUNStack, address, dns string)
options := LC.Tun{
Enable: true,
Device: device,
Stack: stack,
Device: "FlClash",
Stack: tunStack,
DNSHijack: dnsHijack,
AutoRoute: false,
AutoDetectInterface: false,

View File

@@ -14,16 +14,15 @@ extension ArchiveExt on Archive {
final data = entity.readAsBytesSync();
final archiveFile = ArchiveFile(relativePath, data.length, data);
addFile(archiveFile);
} else if (entity is Directory) {
addDirectoryToArchive(entity.path, parentPath);
}
// else if (entity is Directory) {
// addDirectoryToArchive(entity.path, parentPath);
// }
}
}
void addTextFile<T>(String name, T raw) {
final data = json.encode(raw);
addFile(
ArchiveFile.string(name, data),
);
addFile(ArchiveFile.string(name, data));
}
}

View File

@@ -4,6 +4,8 @@ mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
set value(T value) {
if (ref.mounted) {
state = value;
} else {
onUpdate(value);
}
}

View File

@@ -58,8 +58,13 @@ class AppPath {
}
Future<String> get lockFilePath async {
final directory = await dataDir.future;
return join(directory.path, 'FlClash.lock');
final homeDirPath = await appPath.homeDirPath;
return join(homeDirPath, 'FlClash.lock');
}
Future<String> get configFilePath async {
final homeDirPath = await appPath.homeDirPath;
return join(homeDirPath, 'config.yaml');
}
Future<String> get sharedPreferencesPath async {
@@ -77,13 +82,14 @@ class AppPath {
return join(directory, '$id.yaml');
}
Future<String> getProvidersRootPath() async {
final directory = await profilesPath;
return join(directory, 'providers');
}
Future<String> getProvidersDirPath(String id) async {
final directory = await profilesPath;
return join(
directory,
'providers',
id,
);
return join(directory, 'providers', id);
}
Future<String> getProvidersFilePath(
@@ -92,13 +98,7 @@ class AppPath {
String url,
) async {
final directory = await profilesPath;
return join(
directory,
'providers',
id,
type,
url.toMd5(),
);
return join(directory, 'providers', id, type, url.toMd5());
}
Future<String> get tempPath async {

View File

@@ -303,10 +303,7 @@ class AppController {
}
final realTunEnable = _ref.read(realTunEnableProvider);
final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable);
final params = await globalState.getSetupParams(
pathConfig: realPatchConfig,
);
final message = await coreController.setupConfig(params);
final message = await coreController.setupConfig(realPatchConfig);
lastProfileModified = await _ref.read(
currentProfileProvider.select((state) => state?.profileLastModified),
);
@@ -965,7 +962,6 @@ class AppController {
commonPrint.log('$futureFunction ===> $e');
if (realSilence) {
globalState.showNotifier(e.toString());
globalState.showNotifier(e.toString());
} else {
globalState.showMessage(
title: title ?? appLocalizations.tip,

View File

@@ -8,6 +8,7 @@ import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/core/interface.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
@@ -83,8 +84,10 @@ class CoreController {
return await _interface.updateConfig(updateParams);
}
Future<String> setupConfig(SetupParams setupParams) async {
return await _interface.setupConfig(setupParams);
Future<String> setupConfig(ClashConfig clashConfig) async {
await globalState.genConfigFile(clashConfig);
final params = await globalState.getSetupParams();
return await _interface.setupConfig(params);
}
Future<List<Group>> getProxiesGroups() async {
@@ -154,9 +157,6 @@ class CoreController {
if (externalProvidersRawString.isEmpty) {
return null;
}
if (externalProvidersRawString.isEmpty) {
return null;
}
return ExternalProvider.fromJson(json.decode(externalProvidersRawString));
}

View File

@@ -163,7 +163,7 @@ abstract class CoreHandlerInterface with CoreInterface {
@override
Future<Map> getProxies() async {
var map = await _invoke<Map>(method: ActionMethod.getProxies);
final map = await _invoke<Map>(method: ActionMethod.getProxies);
return map ?? {};
}

View File

@@ -73,7 +73,6 @@ class CoreService extends CoreHandlerInterface {
}
void _handleInvokeCrashEvent() {
_socketCompleter = Completer();
coreEventManager.sendEvent(CoreEvent(type: CoreEventType.crash));
}
@@ -134,6 +133,7 @@ class CoreService extends CoreHandlerInterface {
@override
shutdown() async {
await _destroySocket();
_clearCompleter();
if (system.isWindows) {
await request.stopCoreByHelper();
}
@@ -142,11 +142,11 @@ class CoreService extends CoreHandlerInterface {
return true;
}
// void _clearCompleter() {
// for (final completer in _callbackCompleterMap.values) {
// completer.safeCompleter(null);
// }
// }
void _clearCompleter() {
for (final completer in _callbackCompleterMap.values) {
completer.safeCompleter(null);
}
}
@override
Future<bool> preload() async {

View File

@@ -2,6 +2,7 @@
import 'dart:io';
import 'package:fl_clash/common/color.dart';
import 'package:fl_clash/common/system.dart';
import 'package:fl_clash/views/dashboard/widgets/widgets.dart';
import 'package:fl_clash/widgets/widgets.dart';
@@ -95,7 +96,7 @@ extension LogLevelExt on LogLevel {
LogLevel.silent => Colors.grey.shade700,
LogLevel.debug => Colors.grey.shade400,
LogLevel.info => null,
LogLevel.warning => Colors.yellowAccent,
LogLevel.warning => Colors.orangeAccent.darken(),
LogLevel.error => Colors.redAccent,
};
}

View File

@@ -196,6 +196,7 @@ class MessageLookup extends MessageLookupByLibrary {
"copySuccess": MessageLookupByLibrary.simpleMessage("Copy success"),
"core": MessageLookupByLibrary.simpleMessage("Core"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
"coreStatus": MessageLookupByLibrary.simpleMessage("Core status"),
"country": MessageLookupByLibrary.simpleMessage("Country"),
"crashTest": MessageLookupByLibrary.simpleMessage("Crash test"),
"create": MessageLookupByLibrary.simpleMessage("Create"),
@@ -250,6 +251,7 @@ class MessageLookup extends MessageLookupByLibrary {
"dnsDesc": MessageLookupByLibrary.simpleMessage(
"Update DNS related settings",
),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS hijacking"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage(
"Do you want to pass",

View File

@@ -148,6 +148,7 @@ class MessageLookup extends MessageLookupByLibrary {
"copySuccess": MessageLookupByLibrary.simpleMessage("コピー成功"),
"core": MessageLookupByLibrary.simpleMessage("コア"),
"coreInfo": MessageLookupByLibrary.simpleMessage("コア情報"),
"coreStatus": MessageLookupByLibrary.simpleMessage("コアステータス"),
"country": MessageLookupByLibrary.simpleMessage(""),
"crashTest": MessageLookupByLibrary.simpleMessage("クラッシュテスト"),
"create": MessageLookupByLibrary.simpleMessage("作成"),
@@ -188,6 +189,7 @@ class MessageLookup extends MessageLookupByLibrary {
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("新バージョンを発見"),
"discovery": MessageLookupByLibrary.simpleMessage("新しいバージョンを発見"),
"dnsDesc": MessageLookupByLibrary.simpleMessage("DNS関連設定の更新"),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNSハイジャッキング"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNSモード"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage("通過させますか?"),
"domain": MessageLookupByLibrary.simpleMessage("ドメイン"),

View File

@@ -201,6 +201,7 @@ class MessageLookup extends MessageLookupByLibrary {
"copySuccess": MessageLookupByLibrary.simpleMessage("Копирование успешно"),
"core": MessageLookupByLibrary.simpleMessage("Ядро"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Информация о ядре"),
"coreStatus": MessageLookupByLibrary.simpleMessage("Основной статус"),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"crashTest": MessageLookupByLibrary.simpleMessage("Тест на сбои"),
"create": MessageLookupByLibrary.simpleMessage("Создать"),
@@ -257,6 +258,7 @@ class MessageLookup extends MessageLookupByLibrary {
"dnsDesc": MessageLookupByLibrary.simpleMessage(
"Обновление настроек, связанных с DNS",
),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS-перехват"),
"dnsMode": MessageLookupByLibrary.simpleMessage("Режим DNS"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage(
"Вы хотите пропустить",

View File

@@ -138,6 +138,7 @@ class MessageLookup extends MessageLookupByLibrary {
"copySuccess": MessageLookupByLibrary.simpleMessage("复制成功"),
"core": MessageLookupByLibrary.simpleMessage("内核"),
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
"coreStatus": MessageLookupByLibrary.simpleMessage("核心状态"),
"country": MessageLookupByLibrary.simpleMessage("区域"),
"crashTest": MessageLookupByLibrary.simpleMessage("崩溃测试"),
"create": MessageLookupByLibrary.simpleMessage("创建"),
@@ -174,6 +175,7 @@ class MessageLookup extends MessageLookupByLibrary {
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"),
"discovery": MessageLookupByLibrary.simpleMessage("发现新版本"),
"dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS劫持"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"),
"doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"),
"domain": MessageLookupByLibrary.simpleMessage("域名"),

View File

@@ -3304,6 +3304,21 @@ class AppLocalizations {
args: [],
);
}
/// `DNS hijacking`
String get dnsHijacking {
return Intl.message(
'DNS hijacking',
name: 'dnsHijacking',
desc: '',
args: [],
);
}
/// `Core status`
String get coreStatus {
return Intl.message('Core status', name: 'coreStatus', desc: '', args: []);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -29,7 +29,7 @@ Future<void> _service(List<String> flags) async {
_TileListenerWithService(
onStop: () async {
await app?.tip(appLocalizations.stopVpn);
globalState.handleStop();
await globalState.handleStop();
},
),
);
@@ -40,9 +40,8 @@ Future<void> _service(List<String> flags) async {
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
enable: false,
);
final params = await globalState.getSetupParams(pathConfig: clashConfig);
await coreController.setupConfig(params);
await globalState.handleStart();
await coreController.setupConfig(clashConfig);
});
}

View File

@@ -96,6 +96,7 @@ class _CoreContainerState extends ConsumerState<CoreManager>
if (ref.read(coreStatusProvider) != CoreStatus.connected) {
return;
}
context.showNotifier('Core crash');
ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
await coreController.shutdown();
super.onCrash();

View File

@@ -100,17 +100,14 @@ const defaultBypassPrivateRouteAddress = [
'f000::/5',
'f800::/6',
'fe00::/9',
'fec0::/10'
'fec0::/10',
];
@freezed
abstract class ProxyGroup with _$ProxyGroup {
const factory ProxyGroup({
required String name,
@JsonKey(
fromJson: GroupType.parseProfileType,
)
required GroupType type,
@JsonKey(fromJson: GroupType.parseProfileType) required GroupType type,
List<String>? proxies,
List<String>? use,
int? interval,
@@ -132,9 +129,7 @@ abstract class ProxyGroup with _$ProxyGroup {
@freezed
abstract class RuleProvider with _$RuleProvider {
const factory RuleProvider({
required String name,
}) = _RuleProvider;
const factory RuleProvider({required String name}) = _RuleProvider;
factory RuleProvider.fromJson(Map<String, Object?> json) =>
_$RuleProviderFromJson(json);
@@ -206,14 +201,11 @@ extension TunExt on Tun {
? defaultBypassPrivateRouteAddress
: routeAddress;
return switch (system.isDesktop) {
true => copyWith(
autoRoute: true,
routeAddress: [],
),
true => copyWith(autoRoute: true, routeAddress: []),
false => copyWith(
autoRoute: mRouteAddress.isEmpty ? true : false,
routeAddress: mRouteAddress,
),
autoRoute: mRouteAddress.isEmpty ? true : false,
routeAddress: mRouteAddress,
),
};
}
}
@@ -225,11 +217,7 @@ abstract class FallbackFilter with _$FallbackFilter {
@Default('CN') @JsonKey(name: 'geoip-code') String geoipCode,
@Default(['gfw']) List<String> geosite,
@Default(['240.0.0.0/4']) List<String> ipcidr,
@Default([
'+.google.com',
'+.facebook.com',
'+.youtube.com',
])
@Default(['+.google.com', '+.facebook.com', '+.youtube.com'])
List<String> domain,
}) = _FallbackFilter;
@@ -256,32 +244,20 @@ abstract class Dns with _$Dns {
@Default('198.18.0.1/16')
@JsonKey(name: 'fake-ip-range')
String fakeIpRange,
@Default([
'*.lan',
'localhost.ptlogin2.qq.com',
])
@Default(['*.lan', 'localhost.ptlogin2.qq.com'])
@JsonKey(name: 'fake-ip-filter')
List<String> fakeIpFilter,
@Default({
'www.baidu.com': '114.114.114.114',
'+.internal.crop.com': '10.0.0.1',
'geosite:cn': 'https://doh.pub/dns-query'
'geosite:cn': 'https://doh.pub/dns-query',
})
@JsonKey(name: 'nameserver-policy')
Map<String, String> nameserverPolicy,
@Default([
'https://doh.pub/dns-query',
'https://dns.alidns.com/dns-query',
])
@Default(['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'])
List<String> nameserver,
@Default([
'tls://8.8.4.4',
'tls://1.1.1.1',
])
List<String> fallback,
@Default([
'https://doh.pub/dns-query',
])
@Default(['tls://8.8.4.4', 'tls://1.1.1.1']) List<String> fallback,
@Default(['https://doh.pub/dns-query'])
@JsonKey(name: 'proxy-server-nameserver')
List<String> proxyServerNameserver,
@Default(FallbackFilter())
@@ -351,9 +327,7 @@ abstract class ParsedRule with _$ParsedRule {
factory ParsedRule.parseString(String value) {
final splits = value.split(',');
final shortSplits = splits
.where(
(item) => !item.contains('src') && !item.contains('no-resolve'),
)
.where((item) => !item.contains('src') && !item.contains('no-resolve'))
.toList();
final ruleAction = RuleAction.values.firstWhere(
(item) => item.value == shortSplits.first,
@@ -398,23 +372,17 @@ extension ParsedRuleExt on ParsedRule {
if (ruleAction.hasParams) ...[
if (src) 'src',
if (noResolve) 'no-resolve',
]
],
].join(',');
}
}
@freezed
abstract class Rule with _$Rule {
const factory Rule({
required String id,
required String value,
}) = _Rule;
const factory Rule({required String id, required String value}) = _Rule;
factory Rule.value(String value) {
return Rule(
value: value,
id: utils.uuidV4,
);
return Rule(value: value, id: utils.uuidV4);
}
factory Rule.fromJson(Map<String, Object?> json) => _$RuleFromJson(json);
@@ -422,9 +390,7 @@ abstract class Rule with _$Rule {
@freezed
abstract class SubRule with _$SubRule {
const factory SubRule({
required String name,
}) = _SubRule;
const factory SubRule({required String name}) = _SubRule;
factory SubRule.fromJson(Map<String, Object?> json) =>
_$SubRuleFromJson(json);
@@ -434,11 +400,7 @@ List<Rule> _genRule(List<dynamic>? rules) {
if (rules == null) {
return [];
}
return rules
.map(
(item) => Rule.value(item),
)
.toList();
return rules.map((item) => Rule.value(item)).toList();
}
List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
@@ -446,13 +408,7 @@ List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
}
List<SubRule> _genSubRules(Map<String, dynamic> json) {
return json.entries
.map(
(entry) => SubRule(
name: entry.key,
),
)
.toList();
return json.entries.map((entry) => SubRule(name: entry.key)).toList();
}
@freezed
@@ -484,7 +440,7 @@ abstract class ClashConfig with _$ClashConfig {
@Default(false) @JsonKey(name: 'allow-lan') bool allowLan,
@Default(LogLevel.error) @JsonKey(name: 'log-level') LogLevel logLevel,
@Default(false) bool ipv6,
@Default(FindProcessMode.off)
@Default(FindProcessMode.always)
@JsonKey(
name: 'find-process-mode',
unknownEnumValue: FindProcessMode.always,

View File

@@ -97,9 +97,9 @@ extension TrackerInfoExt on TrackerInfo {
final process = metadata.process;
final uid = metadata.uid;
if (uid != 0) {
return '$process($uid)';
return '$process($uid)'.trim();
}
return process;
return process.trim();
}
}
@@ -236,7 +236,7 @@ extension TrafficExt on Traffic {
}
String get desc {
return '${up.traffic.show}${down.traffic.show}';
return '${up.traffic.show} ${down.traffic.show} ';
}
num get speed => up + down;
@@ -249,7 +249,7 @@ abstract class TrafficShow with _$TrafficShow {
}
extension TrafficShowExt on TrafficShow {
String get show => '$value $unit';
String get show => '$value$unit';
}
@freezed

View File

@@ -10,7 +10,6 @@ part 'generated/core.g.dart';
@freezed
abstract class SetupParams with _$SetupParams {
const factory SetupParams({
@JsonKey(name: 'config') required Map<String, dynamic> config,
@JsonKey(name: 'selected-map') required Map<String, String> selectedMap,
@JsonKey(name: 'test-url') required String testUrl,
}) = _SetupParams;
@@ -51,6 +50,7 @@ abstract class VpnOptions with _$VpnOptions {
required bool allowBypass,
required bool systemProxy,
required List<String> bypassDomain,
required String stack,
@Default([]) List<String> routeAddress,
}) = _VpnOptions;

View File

@@ -3765,7 +3765,7 @@ return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that
@JsonSerializable()
class _ClashConfig implements ClashConfig {
const _ClashConfig({@JsonKey(name: 'mixed-port') this.mixedPort = defaultMixedPort, @JsonKey(name: 'socks-port') this.socksPort = 0, @JsonKey(name: 'port') this.port = 0, @JsonKey(name: 'redir-port') this.redirPort = 0, @JsonKey(name: 'tproxy-port') this.tproxyPort = 0, this.mode = Mode.rule, @JsonKey(name: 'allow-lan') this.allowLan = false, @JsonKey(name: 'log-level') this.logLevel = LogLevel.error, this.ipv6 = false, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) this.findProcessMode = FindProcessMode.off, @JsonKey(name: 'keep-alive-interval') this.keepAliveInterval = defaultKeepAliveInterval, @JsonKey(name: 'unified-delay') this.unifiedDelay = true, @JsonKey(name: 'tcp-concurrent') this.tcpConcurrent = true, @JsonKey(fromJson: Tun.safeFormJson) this.tun = defaultTun, @JsonKey(fromJson: Dns.safeDnsFromJson) this.dns = defaultDns, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) this.geoXUrl = defaultGeoXUrl, @JsonKey(name: 'geodata-loader') this.geodataLoader = GeodataLoader.memconservative, @JsonKey(name: 'proxy-groups') final List<ProxyGroup> proxyGroups = const [], final List<String> rule = const [], @JsonKey(name: 'global-ua') this.globalUa, @JsonKey(name: 'external-controller') this.externalController = ExternalControllerStatus.close, final HostsMap hosts = const {}}): _proxyGroups = proxyGroups,_rule = rule,_hosts = hosts;
const _ClashConfig({@JsonKey(name: 'mixed-port') this.mixedPort = defaultMixedPort, @JsonKey(name: 'socks-port') this.socksPort = 0, @JsonKey(name: 'port') this.port = 0, @JsonKey(name: 'redir-port') this.redirPort = 0, @JsonKey(name: 'tproxy-port') this.tproxyPort = 0, this.mode = Mode.rule, @JsonKey(name: 'allow-lan') this.allowLan = false, @JsonKey(name: 'log-level') this.logLevel = LogLevel.error, this.ipv6 = false, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) this.findProcessMode = FindProcessMode.always, @JsonKey(name: 'keep-alive-interval') this.keepAliveInterval = defaultKeepAliveInterval, @JsonKey(name: 'unified-delay') this.unifiedDelay = true, @JsonKey(name: 'tcp-concurrent') this.tcpConcurrent = true, @JsonKey(fromJson: Tun.safeFormJson) this.tun = defaultTun, @JsonKey(fromJson: Dns.safeDnsFromJson) this.dns = defaultDns, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) this.geoXUrl = defaultGeoXUrl, @JsonKey(name: 'geodata-loader') this.geodataLoader = GeodataLoader.memconservative, @JsonKey(name: 'proxy-groups') final List<ProxyGroup> proxyGroups = const [], final List<String> rule = const [], @JsonKey(name: 'global-ua') this.globalUa, @JsonKey(name: 'external-controller') this.externalController = ExternalControllerStatus.close, final HostsMap hosts = const {}}): _proxyGroups = proxyGroups,_rule = rule,_hosts = hosts;
factory _ClashConfig.fromJson(Map<String, dynamic> json) => _$ClashConfigFromJson(json);
@override@JsonKey(name: 'mixed-port') final int mixedPort;

View File

@@ -342,7 +342,7 @@ _ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => _ClashConfig(
json['find-process-mode'],
unknownValue: FindProcessMode.always,
) ??
FindProcessMode.off,
FindProcessMode.always,
keepAliveInterval:
(json['keep-alive-interval'] as num?)?.toInt() ??
defaultKeepAliveInterval,

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SetupParams {
@JsonKey(name: 'config') Map<String, dynamic> get config;@JsonKey(name: 'selected-map') Map<String, String> get selectedMap;@JsonKey(name: 'test-url') String get testUrl;
@JsonKey(name: 'selected-map') Map<String, String> get selectedMap;@JsonKey(name: 'test-url') String get testUrl;
/// Create a copy of SetupParams
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $SetupParamsCopyWith<SetupParams> get copyWith => _$SetupParamsCopyWithImpl<Setu
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SetupParams&&const DeepCollectionEquality().equals(other.config, config)&&const DeepCollectionEquality().equals(other.selectedMap, selectedMap)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
return identical(this, other) || (other.runtimeType == runtimeType&&other is SetupParams&&const DeepCollectionEquality().equals(other.selectedMap, selectedMap)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(config),const DeepCollectionEquality().hash(selectedMap),testUrl);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(selectedMap),testUrl);
@override
String toString() {
return 'SetupParams(config: $config, selectedMap: $selectedMap, testUrl: $testUrl)';
return 'SetupParams(selectedMap: $selectedMap, testUrl: $testUrl)';
}
@@ -48,7 +48,7 @@ abstract mixin class $SetupParamsCopyWith<$Res> {
factory $SetupParamsCopyWith(SetupParams value, $Res Function(SetupParams) _then) = _$SetupParamsCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'config') Map<String, dynamic> config,@JsonKey(name: 'selected-map') Map<String, String> selectedMap,@JsonKey(name: 'test-url') String testUrl
@JsonKey(name: 'selected-map') Map<String, String> selectedMap,@JsonKey(name: 'test-url') String testUrl
});
@@ -65,10 +65,9 @@ class _$SetupParamsCopyWithImpl<$Res>
/// Create a copy of SetupParams
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? config = null,Object? selectedMap = null,Object? testUrl = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? selectedMap = null,Object? testUrl = null,}) {
return _then(_self.copyWith(
config: null == config ? _self.config : config // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,selectedMap: null == selectedMap ? _self.selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable
selectedMap: null == selectedMap ? _self.selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>,testUrl: null == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable
as String,
));
@@ -155,10 +154,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'config') Map<String, dynamic> config, @JsonKey(name: 'selected-map') Map<String, String> selectedMap, @JsonKey(name: 'test-url') String testUrl)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'selected-map') Map<String, String> selectedMap, @JsonKey(name: 'test-url') String testUrl)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SetupParams() when $default != null:
return $default(_that.config,_that.selectedMap,_that.testUrl);case _:
return $default(_that.selectedMap,_that.testUrl);case _:
return orElse();
}
@@ -176,10 +175,10 @@ return $default(_that.config,_that.selectedMap,_that.testUrl);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'config') Map<String, dynamic> config, @JsonKey(name: 'selected-map') Map<String, String> selectedMap, @JsonKey(name: 'test-url') String testUrl) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'selected-map') Map<String, String> selectedMap, @JsonKey(name: 'test-url') String testUrl) $default,) {final _that = this;
switch (_that) {
case _SetupParams():
return $default(_that.config,_that.selectedMap,_that.testUrl);case _:
return $default(_that.selectedMap,_that.testUrl);case _:
throw StateError('Unexpected subclass');
}
@@ -196,10 +195,10 @@ return $default(_that.config,_that.selectedMap,_that.testUrl);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'config') Map<String, dynamic> config, @JsonKey(name: 'selected-map') Map<String, String> selectedMap, @JsonKey(name: 'test-url') String testUrl)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'selected-map') Map<String, String> selectedMap, @JsonKey(name: 'test-url') String testUrl)? $default,) {final _that = this;
switch (_that) {
case _SetupParams() when $default != null:
return $default(_that.config,_that.selectedMap,_that.testUrl);case _:
return $default(_that.selectedMap,_that.testUrl);case _:
return null;
}
@@ -211,16 +210,9 @@ return $default(_that.config,_that.selectedMap,_that.testUrl);case _:
@JsonSerializable()
class _SetupParams implements SetupParams {
const _SetupParams({@JsonKey(name: 'config') required final Map<String, dynamic> config, @JsonKey(name: 'selected-map') required final Map<String, String> selectedMap, @JsonKey(name: 'test-url') required this.testUrl}): _config = config,_selectedMap = selectedMap;
const _SetupParams({@JsonKey(name: 'selected-map') required final Map<String, String> selectedMap, @JsonKey(name: 'test-url') required this.testUrl}): _selectedMap = selectedMap;
factory _SetupParams.fromJson(Map<String, dynamic> json) => _$SetupParamsFromJson(json);
final Map<String, dynamic> _config;
@override@JsonKey(name: 'config') Map<String, dynamic> get config {
if (_config is EqualUnmodifiableMapView) return _config;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_config);
}
final Map<String, String> _selectedMap;
@override@JsonKey(name: 'selected-map') Map<String, String> get selectedMap {
if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap;
@@ -243,16 +235,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SetupParams&&const DeepCollectionEquality().equals(other._config, _config)&&const DeepCollectionEquality().equals(other._selectedMap, _selectedMap)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SetupParams&&const DeepCollectionEquality().equals(other._selectedMap, _selectedMap)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_config),const DeepCollectionEquality().hash(_selectedMap),testUrl);
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_selectedMap),testUrl);
@override
String toString() {
return 'SetupParams(config: $config, selectedMap: $selectedMap, testUrl: $testUrl)';
return 'SetupParams(selectedMap: $selectedMap, testUrl: $testUrl)';
}
@@ -263,7 +255,7 @@ abstract mixin class _$SetupParamsCopyWith<$Res> implements $SetupParamsCopyWith
factory _$SetupParamsCopyWith(_SetupParams value, $Res Function(_SetupParams) _then) = __$SetupParamsCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'config') Map<String, dynamic> config,@JsonKey(name: 'selected-map') Map<String, String> selectedMap,@JsonKey(name: 'test-url') String testUrl
@JsonKey(name: 'selected-map') Map<String, String> selectedMap,@JsonKey(name: 'test-url') String testUrl
});
@@ -280,10 +272,9 @@ class __$SetupParamsCopyWithImpl<$Res>
/// Create a copy of SetupParams
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? config = null,Object? selectedMap = null,Object? testUrl = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? selectedMap = null,Object? testUrl = null,}) {
return _then(_SetupParams(
config: null == config ? _self._config : config // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,selectedMap: null == selectedMap ? _self._selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable
selectedMap: null == selectedMap ? _self._selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>,testUrl: null == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable
as String,
));
@@ -604,7 +595,7 @@ $TunCopyWith<$Res> get tun {
/// @nodoc
mixin _$VpnOptions {
bool get enable; int get port; bool get ipv6; bool get dnsHijacking; AccessControl get accessControl; bool get allowBypass; bool get systemProxy; List<String> get bypassDomain; List<String> get routeAddress;
bool get enable; int get port; bool get ipv6; bool get dnsHijacking; AccessControl get accessControl; bool get allowBypass; bool get systemProxy; List<String> get bypassDomain; String get stack; List<String> get routeAddress;
/// Create a copy of VpnOptions
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -617,16 +608,16 @@ $VpnOptionsCopyWith<VpnOptions> get copyWith => _$VpnOptionsCopyWithImpl<VpnOpti
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is VpnOptions&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.port, port) || other.port == port)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.dnsHijacking, dnsHijacking) || other.dnsHijacking == dnsHijacking)&&(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)&&const DeepCollectionEquality().equals(other.routeAddress, routeAddress));
return identical(this, other) || (other.runtimeType == runtimeType&&other is VpnOptions&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.port, port) || other.port == port)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.dnsHijacking, dnsHijacking) || other.dnsHijacking == dnsHijacking)&&(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.stack, stack) || other.stack == stack)&&const DeepCollectionEquality().equals(other.routeAddress, routeAddress));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,enable,port,ipv6,dnsHijacking,accessControl,allowBypass,systemProxy,const DeepCollectionEquality().hash(bypassDomain),const DeepCollectionEquality().hash(routeAddress));
int get hashCode => Object.hash(runtimeType,enable,port,ipv6,dnsHijacking,accessControl,allowBypass,systemProxy,const DeepCollectionEquality().hash(bypassDomain),stack,const DeepCollectionEquality().hash(routeAddress));
@override
String toString() {
return 'VpnOptions(enable: $enable, port: $port, ipv6: $ipv6, dnsHijacking: $dnsHijacking, accessControl: $accessControl, allowBypass: $allowBypass, systemProxy: $systemProxy, bypassDomain: $bypassDomain, routeAddress: $routeAddress)';
return 'VpnOptions(enable: $enable, port: $port, ipv6: $ipv6, dnsHijacking: $dnsHijacking, accessControl: $accessControl, allowBypass: $allowBypass, systemProxy: $systemProxy, bypassDomain: $bypassDomain, stack: $stack, routeAddress: $routeAddress)';
}
@@ -637,7 +628,7 @@ abstract mixin class $VpnOptionsCopyWith<$Res> {
factory $VpnOptionsCopyWith(VpnOptions value, $Res Function(VpnOptions) _then) = _$VpnOptionsCopyWithImpl;
@useResult
$Res call({
bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, List<String> routeAddress
bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, String stack, List<String> routeAddress
});
@@ -654,7 +645,7 @@ class _$VpnOptionsCopyWithImpl<$Res>
/// Create a copy of VpnOptions
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? port = null,Object? ipv6 = null,Object? dnsHijacking = null,Object? accessControl = null,Object? allowBypass = null,Object? systemProxy = null,Object? bypassDomain = null,Object? routeAddress = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? port = null,Object? ipv6 = null,Object? dnsHijacking = null,Object? accessControl = null,Object? allowBypass = null,Object? systemProxy = null,Object? bypassDomain = null,Object? stack = null,Object? routeAddress = null,}) {
return _then(_self.copyWith(
enable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable
as bool,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable
@@ -664,7 +655,8 @@ as bool,accessControl: null == accessControl ? _self.accessControl : accessContr
as AccessControl,allowBypass: null == allowBypass ? _self.allowBypass : allowBypass // ignore: cast_nullable_to_non_nullable
as bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable
as bool,bypassDomain: null == bypassDomain ? _self.bypassDomain : bypassDomain // ignore: cast_nullable_to_non_nullable
as List<String>,routeAddress: null == routeAddress ? _self.routeAddress : routeAddress // ignore: cast_nullable_to_non_nullable
as List<String>,stack: null == stack ? _self.stack : stack // ignore: cast_nullable_to_non_nullable
as String,routeAddress: null == routeAddress ? _self.routeAddress : routeAddress // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
@@ -759,10 +751,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, List<String> routeAddress)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, String stack, List<String> routeAddress)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _VpnOptions() when $default != null:
return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.routeAddress);case _:
return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.stack,_that.routeAddress);case _:
return orElse();
}
@@ -780,10 +772,10 @@ return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.acce
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, List<String> routeAddress) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, String stack, List<String> routeAddress) $default,) {final _that = this;
switch (_that) {
case _VpnOptions():
return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.routeAddress);case _:
return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.stack,_that.routeAddress);case _:
throw StateError('Unexpected subclass');
}
@@ -800,10 +792,10 @@ return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.acce
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, List<String> routeAddress)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, String stack, List<String> routeAddress)? $default,) {final _that = this;
switch (_that) {
case _VpnOptions() when $default != null:
return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.routeAddress);case _:
return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.stack,_that.routeAddress);case _:
return null;
}
@@ -815,7 +807,7 @@ return $default(_that.enable,_that.port,_that.ipv6,_that.dnsHijacking,_that.acce
@JsonSerializable()
class _VpnOptions implements VpnOptions {
const _VpnOptions({required this.enable, required this.port, required this.ipv6, required this.dnsHijacking, required this.accessControl, required this.allowBypass, required this.systemProxy, required final List<String> bypassDomain, final List<String> routeAddress = const []}): _bypassDomain = bypassDomain,_routeAddress = routeAddress;
const _VpnOptions({required this.enable, required this.port, required this.ipv6, required this.dnsHijacking, required this.accessControl, required this.allowBypass, required this.systemProxy, required final List<String> bypassDomain, required this.stack, final List<String> routeAddress = const []}): _bypassDomain = bypassDomain,_routeAddress = routeAddress;
factory _VpnOptions.fromJson(Map<String, dynamic> json) => _$VpnOptionsFromJson(json);
@override final bool enable;
@@ -832,6 +824,7 @@ class _VpnOptions implements VpnOptions {
return EqualUnmodifiableListView(_bypassDomain);
}
@override final String stack;
final List<String> _routeAddress;
@override@JsonKey() List<String> get routeAddress {
if (_routeAddress is EqualUnmodifiableListView) return _routeAddress;
@@ -853,16 +846,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _VpnOptions&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.port, port) || other.port == port)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.dnsHijacking, dnsHijacking) || other.dnsHijacking == dnsHijacking)&&(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)&&const DeepCollectionEquality().equals(other._routeAddress, _routeAddress));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _VpnOptions&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.port, port) || other.port == port)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.dnsHijacking, dnsHijacking) || other.dnsHijacking == dnsHijacking)&&(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.stack, stack) || other.stack == stack)&&const DeepCollectionEquality().equals(other._routeAddress, _routeAddress));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,enable,port,ipv6,dnsHijacking,accessControl,allowBypass,systemProxy,const DeepCollectionEquality().hash(_bypassDomain),const DeepCollectionEquality().hash(_routeAddress));
int get hashCode => Object.hash(runtimeType,enable,port,ipv6,dnsHijacking,accessControl,allowBypass,systemProxy,const DeepCollectionEquality().hash(_bypassDomain),stack,const DeepCollectionEquality().hash(_routeAddress));
@override
String toString() {
return 'VpnOptions(enable: $enable, port: $port, ipv6: $ipv6, dnsHijacking: $dnsHijacking, accessControl: $accessControl, allowBypass: $allowBypass, systemProxy: $systemProxy, bypassDomain: $bypassDomain, routeAddress: $routeAddress)';
return 'VpnOptions(enable: $enable, port: $port, ipv6: $ipv6, dnsHijacking: $dnsHijacking, accessControl: $accessControl, allowBypass: $allowBypass, systemProxy: $systemProxy, bypassDomain: $bypassDomain, stack: $stack, routeAddress: $routeAddress)';
}
@@ -873,7 +866,7 @@ abstract mixin class _$VpnOptionsCopyWith<$Res> implements $VpnOptionsCopyWith<$
factory _$VpnOptionsCopyWith(_VpnOptions value, $Res Function(_VpnOptions) _then) = __$VpnOptionsCopyWithImpl;
@override @useResult
$Res call({
bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, List<String> routeAddress
bool enable, int port, bool ipv6, bool dnsHijacking, AccessControl accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, String stack, List<String> routeAddress
});
@@ -890,7 +883,7 @@ class __$VpnOptionsCopyWithImpl<$Res>
/// Create a copy of VpnOptions
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? port = null,Object? ipv6 = null,Object? dnsHijacking = null,Object? accessControl = null,Object? allowBypass = null,Object? systemProxy = null,Object? bypassDomain = null,Object? routeAddress = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? port = null,Object? ipv6 = null,Object? dnsHijacking = null,Object? accessControl = null,Object? allowBypass = null,Object? systemProxy = null,Object? bypassDomain = null,Object? stack = null,Object? routeAddress = null,}) {
return _then(_VpnOptions(
enable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable
as bool,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable
@@ -900,7 +893,8 @@ as bool,accessControl: null == accessControl ? _self.accessControl : accessContr
as AccessControl,allowBypass: null == allowBypass ? _self.allowBypass : allowBypass // ignore: cast_nullable_to_non_nullable
as bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable
as bool,bypassDomain: null == bypassDomain ? _self._bypassDomain : bypassDomain // ignore: cast_nullable_to_non_nullable
as List<String>,routeAddress: null == routeAddress ? _self._routeAddress : routeAddress // ignore: cast_nullable_to_non_nullable
as List<String>,stack: null == stack ? _self.stack : stack // ignore: cast_nullable_to_non_nullable
as String,routeAddress: null == routeAddress ? _self._routeAddress : routeAddress // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}

View File

@@ -7,14 +7,12 @@ part of '../core.dart';
// **************************************************************************
_SetupParams _$SetupParamsFromJson(Map<String, dynamic> json) => _SetupParams(
config: json['config'] as Map<String, dynamic>,
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
testUrl: json['test-url'] as String,
);
Map<String, dynamic> _$SetupParamsToJson(_SetupParams instance) =>
<String, dynamic>{
'config': instance.config,
'selected-map': instance.selectedMap,
'test-url': instance.testUrl,
};
@@ -91,6 +89,7 @@ _VpnOptions _$VpnOptionsFromJson(Map<String, dynamic> json) => _VpnOptions(
bypassDomain: (json['bypassDomain'] as List<dynamic>)
.map((e) => e as String)
.toList(),
stack: json['stack'] as String,
routeAddress:
(json['routeAddress'] as List<dynamic>?)
?.map((e) => e as String)
@@ -108,6 +107,7 @@ Map<String, dynamic> _$VpnOptionsToJson(_VpnOptions instance) =>
'allowBypass': instance.allowBypass,
'systemProxy': instance.systemProxy,
'bypassDomain': instance.bypassDomain,
'stack': instance.stack,
'routeAddress': instance.routeAddress,
};

View File

@@ -28,7 +28,7 @@ class Logs extends _$Logs with AutoDisposeNotifierMixin {
}
void addLog(Log value) {
state = state.copyWith()..add(value);
this.value = state.copyWith()..add(value);
}
@override
@@ -50,11 +50,11 @@ class Requests extends _$Requests with AutoDisposeNotifierMixin {
}
void addRequest(TrackerInfo value) {
state = state.copyWith()..add(value);
this.value = state.copyWith()..add(value);
}
}
@riverpod
@Riverpod(keepAlive: true)
class Providers extends _$Providers with AutoDisposeNotifierMixin {
@override
List<ExternalProvider> build() {
@@ -67,10 +67,14 @@ class Providers extends _$Providers with AutoDisposeNotifierMixin {
}
void setProvider(ExternalProvider? provider) {
if (!ref.mounted) {
return;
}
if (provider == null) return;
final index = state.indexWhere((item) => item.name == provider.name);
if (index == -1) return;
state = List.from(state)..[index] = provider;
final newState = List<ExternalProvider>.from(state)..[index] = provider;
value = newState;
}
}
@@ -101,7 +105,7 @@ class SystemBrightness extends _$SystemBrightness
}
void setState(Brightness value) {
state = value;
this.value = value;
}
}
@@ -118,11 +122,11 @@ class Traffics extends _$Traffics with AutoDisposeNotifierMixin {
}
void addTraffic(Traffic value) {
state = state.copyWith()..add(value);
this.value = state.copyWith()..add(value);
}
void clear() {
state = state.copyWith()..clear();
value = state.copyWith()..clear();
}
}
@@ -150,12 +154,6 @@ class LocalIp extends _$LocalIp with AutoDisposeNotifierMixin {
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(localIp: value);
}
@override
set state(String? value) {
super.state = value;
globalState.appState = globalState.appState.copyWith(localIp: state);
}
}
@riverpod
@@ -332,7 +330,7 @@ class DelayDataSource extends _$DelayDataSource with AutoDisposeNotifierMixin {
newDelayMap[delay.url] = {};
}
newDelayMap[delay.url]![delay.name] = delay.value;
state = newDelayMap;
value = newDelayMap;
}
}
}
@@ -375,7 +373,7 @@ class ProfileOverrideState extends _$ProfileOverrideState
if (value == null) {
return;
}
state = value;
this.value = value;
}
}
@@ -403,6 +401,6 @@ class QueryMap extends _$QueryMap with AutoDisposeNotifierMixin {
}
void updateQuery(QueryTag tag, String value) {
state = Map.from(globalState.appState.queryMap)..[tag] = value;
this.value = Map.from(globalState.appState.queryMap)..[tag] = value;
}
}

View File

@@ -14,13 +14,11 @@ class AppSetting extends _$AppSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
appSetting: value,
);
globalState.config = globalState.config.copyWith(appSetting: value);
}
void updateState(AppSettingProps Function(AppSettingProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -33,13 +31,11 @@ class WindowSetting extends _$WindowSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
windowProps: value,
);
globalState.config = globalState.config.copyWith(windowProps: value);
}
void updateState(WindowProps Function(WindowProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -52,13 +48,11 @@ class VpnSetting extends _$VpnSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
vpnProps: value,
);
globalState.config = globalState.config.copyWith(vpnProps: value);
}
void updateState(VpnProps Function(VpnProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -71,13 +65,11 @@ class NetworkSetting extends _$NetworkSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
networkProps: value,
);
globalState.config = globalState.config.copyWith(networkProps: value);
}
void updateState(NetworkProps Function(NetworkProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -90,13 +82,11 @@ class ThemeSetting extends _$ThemeSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
themeProps: value,
);
globalState.config = globalState.config.copyWith(themeProps: value);
}
void updateState(ThemeProps Function(ThemeProps state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -109,15 +99,15 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
profiles: value,
);
globalState.config = globalState.config.copyWith(profiles: value);
}
String? _getLabel(String? label, String id) {
final realLabel = label ?? id;
final hasDup = state.indexWhere(
(element) => element.label == realLabel && element.id != id) !=
final hasDup =
state.indexWhere(
(element) => element.label == realLabel && element.id != id,
) !=
-1;
if (hasDup) {
return _getLabel(utils.getOverwriteLabel(realLabel), id);
@@ -128,8 +118,9 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
void setProfile(Profile profile) {
final List<Profile> profilesTemp = List.from(state);
final index =
profilesTemp.indexWhere((element) => element.id == profile.id);
final index = profilesTemp.indexWhere(
(element) => element.id == profile.id,
);
final updateProfile = profile.copyWith(
label: _getLabel(profile.label, profile.id),
);
@@ -138,21 +129,23 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
} else {
profilesTemp[index] = updateProfile;
}
state = profilesTemp;
value = profilesTemp;
}
void updateProfile(
String profileId, Profile Function(Profile profile) builder) {
String profileId,
Profile Function(Profile profile) builder,
) {
final List<Profile> profilesTemp = List.from(state);
final index = profilesTemp.indexWhere((element) => element.id == profileId);
if (index != -1) {
profilesTemp[index] = builder(profilesTemp[index]);
}
state = profilesTemp;
value = profilesTemp;
}
void deleteProfileById(String id) {
state = state.where((element) => element.id != id).toList();
value = state.where((element) => element.id != id).toList();
}
}
@@ -166,9 +159,7 @@ class CurrentProfileId extends _$CurrentProfileId
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
currentProfileId: value,
);
globalState.config = globalState.config.copyWith(currentProfileId: value);
}
}
@@ -181,13 +172,11 @@ class AppDAVSetting extends _$AppDAVSetting with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
dav: value,
);
globalState.config = globalState.config.copyWith(dav: value);
}
void updateState(DAV? Function(DAV? state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -200,9 +189,7 @@ class OverrideDns extends _$OverrideDns with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
overrideDns: value,
);
globalState.config = globalState.config.copyWith(overrideDns: value);
}
}
@@ -215,9 +202,7 @@ class HotKeyActions extends _$HotKeyActions with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
hotKeyActions: value,
);
globalState.config = globalState.config.copyWith(hotKeyActions: value);
}
}
@@ -231,13 +216,11 @@ class ProxiesStyleSetting extends _$ProxiesStyleSetting
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
proxiesStyle: value,
);
globalState.config = globalState.config.copyWith(proxiesStyle: value);
}
void updateState(ProxiesStyle Function(ProxiesStyle state) builder) {
state = builder(state);
value = builder(state);
}
}
@@ -250,9 +233,7 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
scriptProps: value,
);
globalState.config = globalState.config.copyWith(scriptProps: value);
}
void setScript(Script script) {
@@ -263,15 +244,11 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
} else {
list.add(script);
}
state = state.copyWith(
scripts: list,
);
value = state.copyWith(scripts: list);
}
void setId(String id) {
state = state.copyWith(
currentId: state.currentId != id ? id : null,
);
value = state.copyWith(currentId: state.currentId != id ? id : null);
}
void del(String id) {
@@ -281,10 +258,7 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
list.removeAt(index);
}
final nextId = id == state.currentId ? null : state.currentId;
state = state.copyWith(
scripts: list,
currentId: nextId,
);
state = state.copyWith(scripts: list, currentId: nextId);
}
bool isExits(String label) {
@@ -305,13 +279,11 @@ class PatchClashConfig extends _$PatchClashConfig
if (newState == null) {
return;
}
state = newState;
value = newState;
}
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
patchClashConfig: value,
);
globalState.config = globalState.config.copyWith(patchClashConfig: value);
}
}

View File

@@ -90,7 +90,7 @@ final class LogsProvider extends $NotifierProvider<Logs, FixedList<Log>> {
}
}
String _$logsHash() => r'0a32e067292d449d61af59a689cb26691f4afe44';
String _$logsHash() => r'a671cf70f13d38cae75dc51841b651fe2d2dad9a';
abstract class _$Logs extends $Notifier<FixedList<Log>> {
FixedList<Log> build();
@@ -143,7 +143,7 @@ final class RequestsProvider
}
}
String _$requestsHash() => r'526f2c1da1347fd2e6e3e23aac0335fcfe0cb28a';
String _$requestsHash() => r'8642621b8b5f2e56f3abb04554c058fb30389795';
abstract class _$Requests extends $Notifier<FixedList<TrackerInfo>> {
FixedList<TrackerInfo> build();
@@ -176,7 +176,7 @@ final class ProvidersProvider
argument: null,
retry: null,
name: r'providersProvider',
isAutoDispose: true,
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@@ -197,7 +197,7 @@ final class ProvidersProvider
}
}
String _$providersHash() => r'4292240629a99470b2e72426dde3b9049b9b57e0';
String _$providersHash() => r'b1175e1e2b22e34f8d414386fe672c4933fc47a3';
abstract class _$Providers extends $Notifier<List<ExternalProvider>> {
List<ExternalProvider> build();
@@ -304,7 +304,7 @@ final class SystemBrightnessProvider
}
}
String _$systemBrightnessHash() => r'46eb2d23b05405723efc29480e8f258bf2d8138b';
String _$systemBrightnessHash() => r'2fb112459d5f505768f8c33b314aa62cf1fb0a0a';
abstract class _$SystemBrightness extends $Notifier<Brightness> {
Brightness build();
@@ -357,7 +357,7 @@ final class TrafficsProvider
}
}
String _$trafficsHash() => r'8b86eb718fed5776de174c51fd5b231957011fe6';
String _$trafficsHash() => r'7df7d01f39e9fa1bf629221c9f73273757fa535a';
abstract class _$Traffics extends $Notifier<FixedList<Traffic>> {
FixedList<Traffic> build();
@@ -462,7 +462,7 @@ final class LocalIpProvider extends $NotifierProvider<LocalIp, String?> {
}
}
String _$localIpHash() => r'2dd4afdb29db4791ebd80d976f9ea31c62959199';
String _$localIpHash() => r'25ff07ff9ae316eac7ef39c29d9ae2714b7ba323';
abstract class _$LocalIp extends $Notifier<String?> {
String? build();
@@ -1199,7 +1199,7 @@ final class DelayDataSourceProvider
}
}
String _$delayDataSourceHash() => r'1b94dcfdb9e1eb4c0b7ca69d933f2299d1f94ed5';
String _$delayDataSourceHash() => r'0cc7064c6e7e7a1823df1c5b339001ae49ee54f1';
abstract class _$DelayDataSource extends $Notifier<DelayMap> {
DelayMap build();
@@ -1308,7 +1308,7 @@ final class ProfileOverrideStateProvider
}
String _$profileOverrideStateHash() =>
r'c57bb59c3900b6ee752d6c1cd5c9c0ccc6d1f45e';
r'6bcf739e034cc39623dc63bf304189d63fc19404';
abstract class _$ProfileOverrideState extends $Notifier<ProfileOverrideModel?> {
ProfileOverrideModel? build();
@@ -1414,7 +1414,7 @@ final class QueryMapProvider
}
}
String _$queryMapHash() => r'102d489d31d312f082d7117f80a6de8318eaaf75';
String _$queryMapHash() => r'f64a1bf5fcd4f85986d8ba3c956e397abc4f2d5d';
abstract class _$QueryMap extends $Notifier<Map<QueryTag, String>> {
Map<QueryTag, String> build();

View File

@@ -38,7 +38,7 @@ final class AppSettingProvider
}
}
String _$appSettingHash() => r'13a93334e18b97f5d52eb3e05bbc7b0b8a5c453e';
String _$appSettingHash() => r'7ec7fbf146e690dea42cf854fa4452b2652d8a46';
abstract class _$AppSetting extends $Notifier<AppSettingProps> {
AppSettingProps build();
@@ -91,7 +91,7 @@ final class WindowSettingProvider
}
}
String _$windowSettingHash() => r'9bf31c7e08fab84213f31e249270f9d730bdf711';
String _$windowSettingHash() => r'fc0e5c4ec95a57a24e0e656fc2fab6f31add31e7';
abstract class _$WindowSetting extends $Notifier<WindowProps> {
WindowProps build();
@@ -143,7 +143,7 @@ final class VpnSettingProvider extends $NotifierProvider<VpnSetting, VpnProps> {
}
}
String _$vpnSettingHash() => r'3dae8b56504bfb906aca546c5a5389d79d259a5e';
String _$vpnSettingHash() => r'1dad4881ae7bcec76678585ac7b84f820b2ca92b';
abstract class _$VpnSetting extends $Notifier<VpnProps> {
VpnProps build();
@@ -196,7 +196,7 @@ final class NetworkSettingProvider
}
}
String _$networkSettingHash() => r'5a30d4cbfaba94cc29ad08dc1771ebb368b4ba14';
String _$networkSettingHash() => r'6ac5959ad478247fd60329221743cccc7a7d010b';
abstract class _$NetworkSetting extends $Notifier<NetworkProps> {
NetworkProps build();
@@ -249,7 +249,7 @@ final class ThemeSettingProvider
}
}
String _$themeSettingHash() => r'0b5620b696d73260d94f63cbfb65857acd2000f0';
String _$themeSettingHash() => r'0ddad89cb63fc2b2094dd82262c76d972c2def5c';
abstract class _$ThemeSetting extends $Notifier<ThemeProps> {
ThemeProps build();
@@ -302,7 +302,7 @@ final class ProfilesProvider
}
}
String _$profilesHash() => r'3203cc7de88b91fff86b79c75c2cacd8116fffb7';
String _$profilesHash() => r'aad57222a4a0bd16f2c70f9eb8ba0053d1a26d0f';
abstract class _$Profiles extends $Notifier<List<Profile>> {
List<Profile> build();
@@ -408,7 +408,7 @@ final class AppDAVSettingProvider
}
}
String _$appDAVSettingHash() => r'4bf293ac0d1fba157f60df920b7ffd5afefaab26';
String _$appDAVSettingHash() => r'fa8de5d89d7a11f34f3f8e20b71cf164e5e11888';
abstract class _$AppDAVSetting extends $Notifier<DAV?> {
DAV? build();
@@ -567,7 +567,7 @@ final class ProxiesStyleSettingProvider
}
String _$proxiesStyleSettingHash() =>
r'54ebf20a8d4455b2d7a65824f375c4c02a5fba28';
r'4ff62951ddc8289220191850516b6751ee69d642';
abstract class _$ProxiesStyleSetting extends $Notifier<ProxiesStyle> {
ProxiesStyle build();
@@ -620,7 +620,7 @@ final class ScriptStateProvider
}
}
String _$scriptStateHash() => r'afbb70d1dd7e577b2377ecd8ab35d0905c1d0e87';
String _$scriptStateHash() => r'4770c34c3d24451fef95e372450e4a333b419977';
abstract class _$ScriptState extends $Notifier<ScriptProps> {
ScriptProps build();
@@ -673,7 +673,7 @@ final class PatchClashConfigProvider
}
}
String _$patchClashConfigHash() => r'd9acdd0ace673fc1c1460b63d7a27c5787713c14';
String _$patchClashConfigHash() => r'b355bd89969d4d119631fdf117df230a71493fa8';
abstract class _$PatchClashConfig extends $Notifier<ClashConfig> {
ClashConfig build();

View File

@@ -1,6 +1,8 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi' show Pointer;
import 'dart:io';
import 'dart:isolate';
import 'package:animations/animations.dart';
import 'package:dio/dio.dart';
@@ -17,6 +19,7 @@ import 'package:flutter_js/flutter_js.dart';
import 'package:material_color_utilities/palettes/core_palette.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:yaml_writer/yaml_writer.dart';
import 'common/common.dart';
import 'controller.dart';
@@ -199,6 +202,7 @@ class GlobalState {
final networkProps = config.networkProps;
final port = config.patchClashConfig.mixedPort;
return VpnOptions(
stack: config.patchClashConfig.tun.stack.name,
enable: vpnProps.enable,
systemProxy: networkProps.systemProxy,
port: port,
@@ -253,16 +257,35 @@ class GlobalState {
}
}
Future<SetupParams> getSetupParams({required ClashConfig pathConfig}) async {
final clashConfig = await patchRawConfig(patchConfig: pathConfig);
Future<SetupParams> getSetupParams() async {
final params = SetupParams(
config: clashConfig,
selectedMap: config.currentProfile?.selectedMap ?? {},
testUrl: config.appSetting.testUrl,
);
return params;
}
Future<void> genConfigFile(ClashConfig pathConfig) async {
final configFilePath = await appPath.configFilePath;
final config = await patchRawConfig(patchConfig: pathConfig);
final res = await Isolate.run<String>(() async {
try {
final data = YamlWriter().write(config);
final file = File(configFilePath);
if (!await file.exists()) {
await file.create(recursive: true);
}
await file.writeAsString(data, flush: true);
return '';
} catch (e) {
return e.toString();
}
});
if (res.isNotEmpty) {
throw res;
}
}
AndroidState getAndroidState() {
return AndroidState(
currentProfileName: config.currentProfile?.label ?? '',
@@ -400,7 +423,7 @@ class GlobalState {
rules = [...overrideData.runningRule, ...rules];
}
}
rawConfig['rule'] = rules;
rawConfig['rules'] = rules;
return rawConfig;
}

View File

@@ -258,6 +258,28 @@ class BypassDomainItem extends StatelessWidget {
}
}
class DNSHijackingItem extends ConsumerWidget {
const DNSHijackingItem({super.key});
@override
Widget build(BuildContext context, ref) {
final dnsHijacking = ref.watch(
vpnSettingProvider.select((state) => state.dnsHijacking),
);
return ListItem<RouteMode>.switchItem(
title: Text(appLocalizations.dnsHijacking),
delegate: SwitchDelegate(
value: dnsHijacking,
onChanged: (value) async {
ref
.read(vpnSettingProvider.notifier)
.updateState((state) => state.copyWith(dnsHijacking: value));
},
),
);
}
}
class RouteModeItem extends ConsumerWidget {
const RouteModeItem({super.key});
@@ -344,6 +366,7 @@ final networkItems = [
const BypassDomainItem(),
const AllowBypassItem(),
const Ipv6Item(),
const DNSHijackingItem(),
],
),
if (system.isDesktop)

View File

@@ -50,6 +50,7 @@ class _RequestsViewState extends ConsumerState<RequestsView> {
next,
) {
_requests = next;
updateRequestsThrottler();
});
}

View File

@@ -63,58 +63,64 @@ class _DashboardViewState extends ConsumerState<DashboardView> {
Consumer(
builder: (_, ref, _) {
final coreStatus = ref.watch(coreStatusProvider);
return FilledButton.icon(
onPressed: coreStatus != CoreStatus.disconnected
? () {}
: _handleConnection,
onLongPress: coreStatus != CoreStatus.connected
? null
: _handleConnection,
style: FilledButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 12),
backgroundColor: switch (coreStatus) {
CoreStatus.connecting => null,
CoreStatus.connected => Colors.greenAccent,
CoreStatus.disconnected => context.colorScheme.error,
},
foregroundColor: switch (coreStatus) {
CoreStatus.connecting => null,
CoreStatus.connected => context.colorScheme.onPrimary,
CoreStatus.disconnected => context.colorScheme.onError,
},
),
icon: SizedBox(
height: globalState.measure.bodyMediumHeight,
width: globalState.measure.bodyMediumHeight,
child: FadeRotationScaleBox(
child: switch (coreStatus) {
CoreStatus.connecting => Padding(
padding: EdgeInsets.all(2),
child: CircularProgressIndicator(
key: ValueKey(CoreStatus.connecting),
strokeWidth: 3,
color: context.colorScheme.onPrimary,
backgroundColor: Colors.transparent,
),
),
CoreStatus.connected => Icon(
Icons.check_sharp,
fontWeight: FontWeight.w900,
key: ValueKey(CoreStatus.connected),
),
CoreStatus.disconnected => Icon(
Icons.restart_alt_sharp,
fontWeight: FontWeight.w900,
key: ValueKey(CoreStatus.disconnected),
),
return Tooltip(
message: appLocalizations.coreStatus,
child: FilledButton.icon(
onPressed: coreStatus == CoreStatus.connecting
? () {}
: _handleConnection,
style: FilledButton.styleFrom(
visualDensity: VisualDensity.compact,
padding: EdgeInsets.symmetric(horizontal: 12),
backgroundColor: switch (coreStatus) {
CoreStatus.connecting => null,
CoreStatus.connected => Colors.greenAccent,
CoreStatus.disconnected => context.colorScheme.error,
},
foregroundColor: switch (coreStatus) {
CoreStatus.connecting => null,
CoreStatus.connected => switch (Theme.brightnessOf(
context,
)) {
Brightness.light => context.colorScheme.onSurfaceVariant,
Brightness.dark => null,
},
CoreStatus.disconnected => context.colorScheme.onError,
},
),
icon: SizedBox(
height: globalState.measure.bodyMediumHeight,
width: globalState.measure.bodyMediumHeight,
child: FadeRotationScaleBox(
child: switch (coreStatus) {
CoreStatus.connecting => Padding(
padding: EdgeInsets.all(2),
child: CircularProgressIndicator(
key: ValueKey(CoreStatus.connecting),
strokeWidth: 3,
color: context.colorScheme.onPrimary,
backgroundColor: Colors.transparent,
),
),
CoreStatus.connected => Icon(
Icons.check_sharp,
fontWeight: FontWeight.w900,
key: ValueKey(CoreStatus.connected),
),
CoreStatus.disconnected => Icon(
Icons.restart_alt_sharp,
fontWeight: FontWeight.w900,
key: ValueKey(CoreStatus.disconnected),
),
},
),
),
label: Text(switch (coreStatus) {
CoreStatus.connecting => appLocalizations.connecting,
CoreStatus.connected => appLocalizations.connected,
CoreStatus.disconnected => appLocalizations.disconnected,
}),
),
label: Text(switch (coreStatus) {
CoreStatus.connecting => appLocalizations.connecting,
CoreStatus.connected => appLocalizations.connected,
CoreStatus.disconnected => appLocalizations.disconnected,
}),
);
},
),

View File

@@ -413,10 +413,10 @@ packages:
dependency: "direct main"
description:
name: file_picker
sha256: ef7d2a085c1b1d69d17b6842d0734aad90156de08df6bd3c12496d0bd6ddf8e2
sha256: e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22
url: "https://pub.dev"
source: hosted
version: "10.3.1"
version: "10.3.2"
file_selector_linux:
dependency: transitive
description:
@@ -834,10 +834,10 @@ packages:
dependency: transitive
description:
name: macos_window_utils
sha256: "18745e56b4c0444d802dadbafca98797828b813ee2488e367dd8f84f1ec4c217"
sha256: d4df3501fd32ac0d2d7590cb6a8e4758337d061c8fa0db816fdd636be63a8438
url: "https://pub.dev"
source: hosted
version: "1.8.4"
version: "1.9.0"
matcher:
dependency: transitive
description:
@@ -1455,10 +1455,10 @@ packages:
dependency: "direct main"
description:
name: tray_manager
sha256: ad18c4cd73003097d182884bacb0578ad2865f3ab842a0ad00f6d043ed49eaf0
sha256: "537e539f48cd82d8ee2240d4330158c7b44c7e043e8e18b5811f2f8f6b7df25a"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.5.1"
typed_data:
dependency: transitive
description:
@@ -1690,6 +1690,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.3"
yaml_writer:
dependency: "direct main"
description:
name: yaml_writer
sha256: "69651cd7238411179ac32079937d4aa9a2970150d6b2ae2c6fe6de09402a5dc5"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
sdks:
dart: ">=3.9.0 <4.0.0"
flutter: ">=3.29.0"

View File

@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.88+2025082501
version: 0.8.88+2025082901
environment:
sdk: '>=3.8.0 <4.0.0'
@@ -63,6 +63,7 @@ dependencies:
flutter_cache_manager: ^3.4.1
crypto: ^3.0.3
flutter_acrylic: ^1.1.4
yaml_writer: ^2.1.0
dev_dependencies:
flutter_test:
sdk: flutter