Compare commits
1 Commits
v0.8.88-pr
...
v0.8.88-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9d22d7616 |
@@ -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>
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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?) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ class TempActivity : Activity(),
|
||||
}
|
||||
|
||||
QuickAction.STOP.action -> {
|
||||
launch {
|
||||
State.handleStopServiceAction()
|
||||
}
|
||||
State.handleStopServiceAction()
|
||||
}
|
||||
|
||||
QuickAction.TOGGLE.action -> {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -10,4 +10,7 @@ object Components {
|
||||
|
||||
val TEMP_ACTIVITY =
|
||||
ComponentName(GlobalState.packageName, "${PACKAGE_NAME}.TempActivity")
|
||||
|
||||
val BROADCAST_RECEIVER =
|
||||
ComponentName(GlobalState.packageName, "${PACKAGE_NAME}.BroadcastReceiver")
|
||||
}
|
||||
@@ -10,7 +10,9 @@ enum class QuickAction {
|
||||
}
|
||||
|
||||
enum class BroadcastAction {
|
||||
CREATE_VPN,
|
||||
START,
|
||||
STOP,
|
||||
TOGGLE,
|
||||
}
|
||||
|
||||
enum class AccessControlMode {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
package com.follow.clash.service;
|
||||
|
||||
interface ICallbackInterface {
|
||||
void onResult(String result);
|
||||
void onResult(in byte[] result, boolean isSuccess);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// IMessageInterface.aidl
|
||||
package com.follow.clash.service;
|
||||
|
||||
interface IMessageInterface {
|
||||
void onResult(String result);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 { "," })
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -427,5 +427,7 @@
|
||||
"disconnected": "切断済み",
|
||||
"connecting": "接続中...",
|
||||
"restartCoreTip": "コアを再起動してもよろしいですか?",
|
||||
"forceRestartCoreTip": "コアを強制再起動してもよろしいですか?"
|
||||
"forceRestartCoreTip": "コアを強制再起動してもよろしいですか?",
|
||||
"dnsHijacking": "DNSハイジャッキング",
|
||||
"coreStatus": "コアステータス"
|
||||
}
|
||||
@@ -427,5 +427,7 @@
|
||||
"disconnected": "Отключено",
|
||||
"connecting": "Подключение...",
|
||||
"restartCoreTip": "Вы уверены, что хотите перезапустить ядро?",
|
||||
"forceRestartCoreTip": "Вы уверены, что хотите принудительно перезапустить ядро?"
|
||||
"forceRestartCoreTip": "Вы уверены, что хотите принудительно перезапустить ядро?",
|
||||
"dnsHijacking": "DNS-перехват",
|
||||
"coreStatus": "Основной статус"
|
||||
}
|
||||
@@ -427,5 +427,7 @@
|
||||
"disconnected": "已断开",
|
||||
"connecting": "连接中...",
|
||||
"restartCoreTip": "您确定要重启核心吗?",
|
||||
"forceRestartCoreTip": "您确定要强制重启核心吗?"
|
||||
"forceRestartCoreTip": "您确定要强制重启核心吗?",
|
||||
"dnsHijacking": "DNS劫持",
|
||||
"coreStatus": "核心状态"
|
||||
}
|
||||
|
||||
Submodule core/Clash.Meta updated: 82906c837a...6fe704ae11
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
17
core/lib.go
17
core/lib.go
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
|
||||
set value(T value) {
|
||||
if (ref.mounted) {
|
||||
state = value;
|
||||
} else {
|
||||
onUpdate(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ?? {};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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("ドメイン"),
|
||||
|
||||
@@ -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(
|
||||
"Вы хотите пропустить",
|
||||
|
||||
@@ -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("域名"),
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -50,6 +50,7 @@ class _RequestsViewState extends ConsumerState<RequestsView> {
|
||||
next,
|
||||
) {
|
||||
_requests = next;
|
||||
updateRequestsThrottler();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
20
pubspec.lock
20
pubspec.lock
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user