Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a333794ceb | ||
|
|
d89481114f | ||
|
|
9dcb53b2fe | ||
|
|
be4eaf4d52 | ||
|
|
72bef5f672 |
2
.github/workflows/build.yaml
vendored
2
.github/workflows/build.yaml
vendored
@@ -79,7 +79,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'core/go.mod'
|
||||
go-version: 'stable'
|
||||
cache-dependency-path: |
|
||||
core/go.sum
|
||||
|
||||
|
||||
55
.github/workflows/change.yaml
vendored
55
.github/workflows/change.yaml
vendored
@@ -16,30 +16,49 @@ jobs:
|
||||
|
||||
- name: Generate
|
||||
run: |
|
||||
tags=$(git tag --sort=creatordate)
|
||||
previous=""
|
||||
if [ ! -f CHANGELOG.md ]; then
|
||||
echo "" > CHANGELOG.md
|
||||
else
|
||||
previous=$(grep -oP '^## \K.*' CHANGELOG.md | tail -n 1)
|
||||
fi
|
||||
for tag in $tags; do
|
||||
if [ -n "$previous" ]; then
|
||||
echo "## $tag" >> CHANGELOG.md
|
||||
git log --pretty=format:"* %s (%h)" "$previous..$tag" >> CHANGELOG.md
|
||||
echo -e "\n" >> CHANGELOG.md
|
||||
tags=($(git tag --merged $(git rev-parse HEAD) --sort=-creatordate))
|
||||
preTag=$(grep -oP '^## \K.*' CHANGELOG.md | head -n 1)
|
||||
currentTag=""
|
||||
for ((i = 0; i <= ${#tags[@]}; i++)); do
|
||||
if (( i < ${#tags[@]} )); then
|
||||
tag=${tags[$i]}
|
||||
else
|
||||
tag=""
|
||||
fi
|
||||
previous=$tag
|
||||
if [ -n "$currentTag" ]; then
|
||||
if [ "$(echo -e "$currentTag\n$preTag" | sort -V | head -n 1)" == "$currentTag" ]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
if [ -n "$currentTag" ]; then
|
||||
echo "## $currentTag" >> CHANGELOG.md
|
||||
echo "" >> CHANGELOG.md
|
||||
if [ -n "$tag" ]; then
|
||||
git log --pretty=format:"%B" "$tag..$currentTag" | awk 'NF {print "- " $0} !NF {print ""}' >> CHANGELOG.md
|
||||
else
|
||||
git log --pretty=format:"%B" "$currentTag" | awk 'NF {print "- " $0} !NF {print ""}' >> CHANGELOG.md
|
||||
fi
|
||||
echo "" >> CHANGELOG.md
|
||||
fi
|
||||
currentTag=$tag
|
||||
done
|
||||
|
||||
- name: Commit
|
||||
run: |
|
||||
if !git diff --cached --quiet; then
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git add CHANGELOG.md
|
||||
git commit -m "Update Changelog"
|
||||
git add CHANGELOG.md
|
||||
if ! git diff --cached --quiet; then
|
||||
echo "Commit pushing"
|
||||
git config --local user.email "chen08209@gmail.com"
|
||||
git config --local user.name "chen08209"
|
||||
git commit -m "Update changelog"
|
||||
git push
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Push succeeded"
|
||||
else
|
||||
echo "Push failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
52
CHANGELOG.md
52
CHANGELOG.md
@@ -1,4 +1,46 @@
|
||||
## v0.8.63
|
||||
## v0.8.67
|
||||
|
||||
- Add android shortcuts
|
||||
|
||||
- Fix init params issues
|
||||
|
||||
- Fix dynamic color issues
|
||||
|
||||
- Optimize navigator animate
|
||||
|
||||
- Optimize window init
|
||||
|
||||
- Optimize fab
|
||||
|
||||
- Optimize save
|
||||
|
||||
## v0.8.66
|
||||
|
||||
- Fix the collapse issues
|
||||
|
||||
- Add fontFamily options
|
||||
|
||||
## v0.8.65
|
||||
|
||||
- Update core version
|
||||
|
||||
- Update flutter version
|
||||
|
||||
- Optimize ip check
|
||||
|
||||
- Optimize url-test
|
||||
|
||||
## v0.8.64
|
||||
|
||||
- Update release message
|
||||
|
||||
- Init auto gen changelog
|
||||
|
||||
- Fix windows tray issues
|
||||
|
||||
- Fix urltest issues
|
||||
|
||||
- Add auto changelog
|
||||
|
||||
- Fix windows admin auto launch issues
|
||||
|
||||
@@ -10,8 +52,6 @@
|
||||
|
||||
- Fix some issues
|
||||
|
||||
## v0.8.62
|
||||
|
||||
- Optimize ip detection
|
||||
|
||||
- Support android vpn ipv6 inbound switch
|
||||
@@ -28,12 +68,6 @@
|
||||
|
||||
- Update readme
|
||||
|
||||
- Update README.md 2
|
||||
|
||||
- Update README.md 2
|
||||
|
||||
- Update README.md
|
||||
|
||||
## v0.8.60
|
||||
|
||||
- Fix build error2
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
|
||||
<uses-permission
|
||||
android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
|
||||
tools:ignore="SystemPermissionTypo" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
@@ -23,8 +24,8 @@
|
||||
|
||||
<application
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="FlClash">
|
||||
<activity
|
||||
android:name="com.follow.clash.MainActivity"
|
||||
@@ -73,11 +74,11 @@
|
||||
android:theme="@style/TransparentTheme">
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<action android:name="com.follow.clash.action.START" />
|
||||
<action android:name="${applicationId}.action.STOP" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<action android:name="com.follow.clash.action.STOP" />
|
||||
<action android:name="${applicationId}.action.CHANGE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import android.content.Context
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.follow.clash.plugins.AppPlugin
|
||||
import com.follow.clash.plugins.ServicePlugin
|
||||
import com.follow.clash.plugins.VpnPlugin
|
||||
import com.follow.clash.plugins.TilePlugin
|
||||
import com.follow.clash.plugins.VpnPlugin
|
||||
import io.flutter.FlutterInjector
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.embedding.engine.dart.DartExecutor
|
||||
@@ -33,6 +33,10 @@ object GlobalState {
|
||||
return currentEngine?.plugins?.get(AppPlugin::class.java) as AppPlugin?
|
||||
}
|
||||
|
||||
fun getText(text: String): String {
|
||||
return getCurrentAppPlugin()?.getText(text) ?: ""
|
||||
}
|
||||
|
||||
fun getCurrentTilePlugin(): TilePlugin? {
|
||||
val currentEngine = if (flutterEngine != null) flutterEngine else serviceEngine
|
||||
return currentEngine?.plugins?.get(TilePlugin::class.java) as TilePlugin?
|
||||
@@ -42,6 +46,27 @@ object GlobalState {
|
||||
return serviceEngine?.plugins?.get(VpnPlugin::class.java) as VpnPlugin?
|
||||
}
|
||||
|
||||
fun handleToggle(context: Context) {
|
||||
if (runState.value == RunState.STOP) {
|
||||
runState.value = RunState.PENDING
|
||||
val tilePlugin = getCurrentTilePlugin()
|
||||
if (tilePlugin != null) {
|
||||
tilePlugin.handleStart()
|
||||
} else {
|
||||
initServiceEngine(context)
|
||||
}
|
||||
} else {
|
||||
handleStop()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleStop() {
|
||||
if (runState.value == RunState.START) {
|
||||
runState.value = RunState.PENDING
|
||||
getCurrentTilePlugin()?.handleStop()
|
||||
}
|
||||
}
|
||||
|
||||
fun destroyServiceEngine() {
|
||||
serviceEngine?.destroy()
|
||||
serviceEngine = null
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.follow.clash
|
||||
|
||||
import com.follow.clash.plugins.AppPlugin
|
||||
import com.follow.clash.plugins.ServicePlugin
|
||||
import com.follow.clash.plugins.VpnPlugin
|
||||
import com.follow.clash.plugins.TilePlugin
|
||||
import com.follow.clash.plugins.VpnPlugin
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
|
||||
|
||||
@@ -2,17 +2,18 @@ package com.follow.clash
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import com.follow.clash.extensions.wrapAction
|
||||
|
||||
class TempActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
when (intent.action) {
|
||||
"com.follow.clash.action.START" -> {
|
||||
GlobalState.getCurrentTilePlugin()?.handleStart()
|
||||
wrapAction("STOP") -> {
|
||||
GlobalState.handleStop()
|
||||
}
|
||||
|
||||
"com.follow.clash.action.STOP" -> {
|
||||
GlobalState.getCurrentTilePlugin()?.handleStop()
|
||||
wrapAction("CHANGE") -> {
|
||||
GlobalState.handleToggle(applicationContext)
|
||||
}
|
||||
}
|
||||
finishAndRemoveTask()
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
package com.follow.clash.extensions
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.os.Build
|
||||
import android.system.OsConstants.IPPROTO_TCP
|
||||
import android.system.OsConstants.IPPROTO_UDP
|
||||
import android.util.Base64
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.follow.clash.TempActivity
|
||||
import com.follow.clash.models.CIDR
|
||||
import com.follow.clash.models.Metadata
|
||||
import com.follow.clash.models.VpnOptions
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.net.Inet4Address
|
||||
import java.net.Inet6Address
|
||||
import java.net.InetAddress
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
||||
suspend fun Drawable.getBase64(): String {
|
||||
@@ -34,6 +43,40 @@ fun Metadata.getProtocol(): Int? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun VpnOptions.getIpv4RouteAddress(): List<CIDR> {
|
||||
return routeAddress.filter {
|
||||
it.isIpv4()
|
||||
}.map {
|
||||
it.toCIDR()
|
||||
}
|
||||
}
|
||||
|
||||
fun VpnOptions.getIpv6RouteAddress(): List<CIDR> {
|
||||
return routeAddress.filter {
|
||||
it.isIpv6()
|
||||
}.map {
|
||||
it.toCIDR()
|
||||
}
|
||||
}
|
||||
|
||||
fun String.isIpv4(): Boolean {
|
||||
val parts = split("/")
|
||||
if (parts.size != 2) {
|
||||
throw IllegalArgumentException("Invalid CIDR format")
|
||||
}
|
||||
val address = InetAddress.getByName(parts[0])
|
||||
return address.address.size == 4
|
||||
}
|
||||
|
||||
fun String.isIpv6(): Boolean {
|
||||
val parts = split("/")
|
||||
if (parts.size != 2) {
|
||||
throw IllegalArgumentException("Invalid CIDR format")
|
||||
}
|
||||
val address = InetAddress.getByName(parts[0])
|
||||
return address.address.size == 16
|
||||
}
|
||||
|
||||
fun String.toCIDR(): CIDR {
|
||||
val parts = split("/")
|
||||
if (parts.size != 2) {
|
||||
@@ -71,6 +114,34 @@ fun InetAddress.asSocketAddressText(port: Int): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.wrapAction(action: String): String {
|
||||
return "${this.packageName}.action.$action"
|
||||
}
|
||||
|
||||
fun Context.getActionIntent(action: String): Intent {
|
||||
val actionIntent = Intent(this, TempActivity::class.java)
|
||||
actionIntent.action = wrapAction(action)
|
||||
return actionIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
|
||||
}
|
||||
|
||||
fun Context.getActionPendingIntent(action: String): PendingIntent {
|
||||
return if (Build.VERSION.SDK_INT >= 31) {
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
getActionIntent(action),
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
} else {
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
getActionIntent(action),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun numericToTextFormat(src: ByteArray): String {
|
||||
val sb = StringBuilder(39)
|
||||
@@ -87,3 +158,25 @@ private fun numericToTextFormat(src: ByteArray): String {
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
suspend fun <T> MethodChannel.awaitResult(
|
||||
method: String,
|
||||
arguments: Any? = null
|
||||
): T? = withContext(Dispatchers.Main) { // 切换到主线程
|
||||
suspendCoroutine { continuation ->
|
||||
invokeMethod(method, arguments, object : MethodChannel.Result {
|
||||
override fun success(result: Any?) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
continuation.resume(result as T)
|
||||
}
|
||||
|
||||
override fun error(code: String, message: String?, details: Any?) {
|
||||
continuation.resume(null)
|
||||
}
|
||||
|
||||
override fun notImplemented() {
|
||||
continuation.resume(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,7 @@ package com.follow.clash.models
|
||||
import java.net.InetAddress
|
||||
|
||||
enum class AccessControlMode {
|
||||
acceptSelected,
|
||||
rejectSelected,
|
||||
acceptSelected, rejectSelected,
|
||||
}
|
||||
|
||||
data class AccessControl(
|
||||
@@ -22,6 +21,7 @@ data class VpnOptions(
|
||||
val allowBypass: Boolean,
|
||||
val systemProxy: Boolean,
|
||||
val bypassDomain: List<String>,
|
||||
val routeAddress: List<String>,
|
||||
val ipv4Address: String,
|
||||
val ipv6Address: String,
|
||||
val dnsServerAddress: String,
|
||||
|
||||
@@ -14,9 +14,15 @@ import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.R
|
||||
import com.follow.clash.extensions.awaitResult
|
||||
import com.follow.clash.extensions.getActionIntent
|
||||
import com.follow.clash.extensions.getBase64
|
||||
import com.follow.clash.models.Package
|
||||
import com.google.gson.Gson
|
||||
@@ -31,6 +37,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.util.zip.ZipFile
|
||||
@@ -116,11 +123,21 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
scope = CoroutineScope(Dispatchers.Default)
|
||||
context = flutterPluginBinding.applicationContext;
|
||||
context = flutterPluginBinding.applicationContext
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "app")
|
||||
channel.setMethodCallHandler(this)
|
||||
}
|
||||
|
||||
private fun initShortcuts(label: String) {
|
||||
val shortcut = ShortcutInfoCompat.Builder(context, "toggle")
|
||||
.setShortLabel(label)
|
||||
.setIcon(IconCompat.createWithResource(context, R.mipmap.ic_launcher_round))
|
||||
.setIntent(context.getActionIntent("CHANGE"))
|
||||
.build()
|
||||
ShortcutManagerCompat.setDynamicShortcuts(context, listOf(shortcut))
|
||||
}
|
||||
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
scope.cancel()
|
||||
@@ -128,11 +145,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
|
||||
private fun tip(message: String?) {
|
||||
if (GlobalState.flutterEngine == null) {
|
||||
if (toast != null) {
|
||||
toast!!.cancel()
|
||||
}
|
||||
toast = Toast.makeText(context, message, Toast.LENGTH_SHORT)
|
||||
toast!!.show()
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +153,18 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
when (call.method) {
|
||||
"moveTaskToBack" -> {
|
||||
activity?.moveTaskToBack(true)
|
||||
result.success(true);
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
"updateExcludeFromRecents" -> {
|
||||
val value = call.argument<Boolean>("value")
|
||||
updateExcludeFromRecents(value)
|
||||
result.success(true);
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
"initShortcuts" -> {
|
||||
initShortcuts(call.arguments as String)
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
"getPackages" -> {
|
||||
@@ -197,7 +215,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
}
|
||||
|
||||
else -> {
|
||||
result.notImplemented();
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,7 +288,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
|
||||
private fun getPackages(): List<Package> {
|
||||
val packageManager = context.packageManager
|
||||
if (packages.isNotEmpty()) return packages;
|
||||
if (packages.isNotEmpty()) return packages
|
||||
packageManager?.getInstalledPackages(PackageManager.GET_META_DATA)?.filter {
|
||||
it.packageName != context.packageName
|
||||
|| it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
|
||||
@@ -284,7 +302,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
firstInstallTime = it.firstInstallTime
|
||||
)
|
||||
}?.let { packages.addAll(it) }
|
||||
return packages;
|
||||
return packages
|
||||
}
|
||||
|
||||
private suspend fun getPackagesToJson(): String {
|
||||
@@ -306,7 +324,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
val intent = VpnService.prepare(context)
|
||||
if (intent != null) {
|
||||
activity?.startActivityForResult(intent, VPN_PERMISSION_REQUEST_CODE)
|
||||
return;
|
||||
return
|
||||
}
|
||||
vpnCallBack?.invoke()
|
||||
}
|
||||
@@ -330,6 +348,12 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
}
|
||||
}
|
||||
|
||||
fun getText(text: String): String? {
|
||||
return runBlocking {
|
||||
channel.awaitResult<String>("getText", text)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isChinaPackage(packageName: String): Boolean {
|
||||
val packageManager = context.packageManager ?: return false
|
||||
skipPrefixList.forEach {
|
||||
@@ -398,7 +422,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
}
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
activity = binding.activity;
|
||||
activity = binding.activity
|
||||
binding.addActivityResultListener(::onActivityResult)
|
||||
binding.addRequestPermissionsResultListener(::onRequestPermissionsResultListener)
|
||||
}
|
||||
@@ -408,7 +432,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||
activity = binding.activity;
|
||||
activity = binding.activity
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.follow.clash.plugins
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import androidx.core.content.getSystemService
|
||||
import com.follow.clash.GlobalState
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
|
||||
@@ -13,8 +13,13 @@ import android.os.Build
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.follow.clash.BaseServiceInterface
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.MainActivity
|
||||
import com.follow.clash.extensions.getActionPendingIntent
|
||||
import com.follow.clash.models.VpnOptions
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class FlClashService : Service(), BaseServiceInterface {
|
||||
@@ -64,6 +69,11 @@ class FlClashService : Service(), BaseServiceInterface {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
|
||||
}
|
||||
addAction(
|
||||
0,
|
||||
GlobalState.getText("stop"),
|
||||
getActionPendingIntent("STOP")
|
||||
)
|
||||
setOngoing(true)
|
||||
setShowWhen(false)
|
||||
setOnlyAlertOnce(true)
|
||||
@@ -82,21 +92,23 @@ class FlClashService : Service(), BaseServiceInterface {
|
||||
|
||||
@SuppressLint("ForegroundServiceType", "WrongConstant")
|
||||
override fun startForeground(title: String, content: String) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
var channel = manager?.getNotificationChannel(CHANNEL)
|
||||
if (channel == null) {
|
||||
channel =
|
||||
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
|
||||
manager?.createNotificationChannel(channel)
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
var channel = manager?.getNotificationChannel(CHANNEL)
|
||||
if (channel == null) {
|
||||
channel =
|
||||
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
|
||||
manager?.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
val notification =
|
||||
notificationBuilder.setContentTitle(title).setContentText(content).build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
}
|
||||
val notification =
|
||||
notificationBuilder.setContentTitle(title).setContentText(content).build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.service.quicksettings.Tile
|
||||
import android.service.quicksettings.TileService
|
||||
import androidx.annotation.RequiresApi
|
||||
@@ -67,19 +66,7 @@ class FlClashTileService : TileService() {
|
||||
override fun onClick() {
|
||||
super.onClick()
|
||||
activityTransfer()
|
||||
if (GlobalState.runState.value == RunState.STOP) {
|
||||
GlobalState.runState.value = RunState.PENDING
|
||||
val tilePlugin = GlobalState.getCurrentTilePlugin()
|
||||
if (tilePlugin != null) {
|
||||
tilePlugin.handleStart()
|
||||
} else {
|
||||
GlobalState.initServiceEngine(applicationContext)
|
||||
}
|
||||
} else if (GlobalState.runState.value == RunState.START) {
|
||||
GlobalState.runState.value = RunState.PENDING
|
||||
GlobalState.getCurrentTilePlugin()?.handleStop()
|
||||
}
|
||||
|
||||
GlobalState.handleToggle(applicationContext)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
||||
@@ -15,12 +15,15 @@ import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.Parcel
|
||||
import android.os.RemoteException
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.follow.clash.BaseServiceInterface
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.MainActivity
|
||||
import com.follow.clash.R
|
||||
import com.follow.clash.TempActivity
|
||||
import com.follow.clash.extensions.getActionPendingIntent
|
||||
import com.follow.clash.extensions.getIpv4RouteAddress
|
||||
import com.follow.clash.extensions.getIpv6RouteAddress
|
||||
import com.follow.clash.extensions.toCIDR
|
||||
import com.follow.clash.models.AccessControlMode
|
||||
import com.follow.clash.models.VpnOptions
|
||||
@@ -40,12 +43,28 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
if (options.ipv4Address.isNotEmpty()) {
|
||||
val cidr = options.ipv4Address.toCIDR()
|
||||
addAddress(cidr.address, cidr.prefixLength)
|
||||
addRoute("0.0.0.0", 0)
|
||||
val routeAddress = options.getIpv4RouteAddress()
|
||||
if (routeAddress.isNotEmpty()) {
|
||||
routeAddress.forEach { i ->
|
||||
Log.d("addRoute4", "address: ${i.address} prefixLength:${i.prefixLength}")
|
||||
addRoute(i.address, i.prefixLength)
|
||||
}
|
||||
} else {
|
||||
addRoute("0.0.0.0", 0)
|
||||
}
|
||||
}
|
||||
if (options.ipv6Address.isNotEmpty()) {
|
||||
val cidr = options.ipv6Address.toCIDR()
|
||||
addAddress(cidr.address, cidr.prefixLength)
|
||||
addRoute("::", 0)
|
||||
val routeAddress = options.getIpv6RouteAddress()
|
||||
if (routeAddress.isNotEmpty()) {
|
||||
routeAddress.forEach { i ->
|
||||
Log.d("addRoute6", "address: ${i.address} prefixLength:${i.prefixLength}")
|
||||
addRoute(i.address, i.prefixLength)
|
||||
}
|
||||
} else {
|
||||
addRoute("::", 0)
|
||||
}
|
||||
}
|
||||
addDnsServer(options.dnsServerAddress)
|
||||
setMtu(9000)
|
||||
@@ -122,26 +141,6 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
)
|
||||
}
|
||||
|
||||
val stopIntent = Intent(this, TempActivity::class.java)
|
||||
stopIntent.action = "com.follow.clash.action.STOP"
|
||||
stopIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
|
||||
|
||||
|
||||
val stopPendingIntent = if (Build.VERSION.SDK_INT >= 31) {
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
stopIntent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
} else {
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
stopIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
with(NotificationCompat.Builder(this, CHANNEL)) {
|
||||
setSmallIcon(R.drawable.ic_stat_name)
|
||||
setContentTitle("FlClash")
|
||||
@@ -152,30 +151,39 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
|
||||
}
|
||||
setOngoing(true)
|
||||
addAction(
|
||||
0,
|
||||
GlobalState.getText("stop"),
|
||||
getActionPendingIntent("STOP")
|
||||
)
|
||||
setShowWhen(false)
|
||||
setOnlyAlertOnce(true)
|
||||
setAutoCancel(true)
|
||||
addAction(0, "Stop", stopPendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ForegroundServiceType", "WrongConstant")
|
||||
override fun startForeground(title: String, content: String) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
var channel = manager?.getNotificationChannel(CHANNEL)
|
||||
if (channel == null) {
|
||||
channel =
|
||||
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
|
||||
manager?.createNotificationChannel(channel)
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
var channel = manager?.getNotificationChannel(CHANNEL)
|
||||
if (channel == null) {
|
||||
channel =
|
||||
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
|
||||
manager?.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
val notification =
|
||||
notificationBuilder
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
}
|
||||
val notification =
|
||||
notificationBuilder.setContentTitle(title).setContentText(content).build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Submodule core/Clash.Meta updated: 8472840f47...148f1a2445
175
core/common.go
175
core/common.go
@@ -66,12 +66,13 @@ type ProcessMapItem struct {
|
||||
}
|
||||
|
||||
type ExternalProvider struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
VehicleType string `json:"vehicle-type"`
|
||||
Count int `json:"count"`
|
||||
Path string `json:"path"`
|
||||
UpdateAt time.Time `json:"update-at"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
VehicleType string `json:"vehicle-type"`
|
||||
Count int `json:"count"`
|
||||
Path string `json:"path"`
|
||||
UpdateAt time.Time `json:"update-at"`
|
||||
SubscriptionInfo *provider.SubscriptionInfo `json:"subscription-info"`
|
||||
}
|
||||
|
||||
type ExternalProviders []ExternalProvider
|
||||
@@ -198,12 +199,13 @@ func toExternalProvider(p cp.Provider) (*ExternalProvider, error) {
|
||||
case *provider.ProxySetProvider:
|
||||
psp := p.(*provider.ProxySetProvider)
|
||||
return &ExternalProvider{
|
||||
Name: psp.Name(),
|
||||
Type: psp.Type().String(),
|
||||
VehicleType: psp.VehicleType().String(),
|
||||
Count: psp.Count(),
|
||||
Path: psp.Vehicle().Path(),
|
||||
UpdateAt: psp.UpdatedAt(),
|
||||
Name: psp.Name(),
|
||||
Type: psp.Type().String(),
|
||||
VehicleType: psp.VehicleType().String(),
|
||||
Count: psp.Count(),
|
||||
UpdateAt: psp.UpdatedAt(),
|
||||
Path: psp.Vehicle().Path(),
|
||||
SubscriptionInfo: psp.GetSubscriptionInfo(),
|
||||
}, nil
|
||||
case *rp.RuleSetProvider:
|
||||
rsp := p.(*rp.RuleSetProvider)
|
||||
@@ -212,8 +214,8 @@ func toExternalProvider(p cp.Provider) (*ExternalProvider, error) {
|
||||
Type: rsp.Type().String(),
|
||||
VehicleType: rsp.VehicleType().String(),
|
||||
Count: rsp.Count(),
|
||||
Path: rsp.Vehicle().Path(),
|
||||
UpdateAt: rsp.UpdatedAt(),
|
||||
Path: rsp.Vehicle().Path(),
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.New("not external provider")
|
||||
@@ -247,152 +249,6 @@ func decorationConfig(profileId string, cfg config.RawConfig) *config.RawConfig
|
||||
return prof
|
||||
}
|
||||
|
||||
//func Reduce[T any, U any](s []T, initVal U, f func(U, T) U) U {
|
||||
// for _, v := range s {
|
||||
// initVal = f(initVal, v)
|
||||
// }
|
||||
// return initVal
|
||||
//}
|
||||
//
|
||||
//func Map[T, U any](slice []T, fn func(T) U) []U {
|
||||
// result := make([]U, len(slice))
|
||||
// for i, v := range slice {
|
||||
// result[i] = fn(v)
|
||||
// }
|
||||
// return result
|
||||
//}
|
||||
//
|
||||
//func replaceFromMap(s string, m map[string]string) string {
|
||||
// for k, v := range m {
|
||||
// s = strings.ReplaceAll(s, k, v)
|
||||
// }
|
||||
// return s
|
||||
//}
|
||||
//
|
||||
//func removeDuplicateFromSlice[T any](slice []T) []T {
|
||||
// result := make([]T, 0)
|
||||
// seen := make(map[any]struct{})
|
||||
// for _, value := range slice {
|
||||
// if _, ok := seen[value]; !ok {
|
||||
// result = append(result, value)
|
||||
// seen[value] = struct{}{}
|
||||
// }
|
||||
// }
|
||||
// return result
|
||||
//}
|
||||
|
||||
//func generateProxyGroupAndRule(proxyGroup *[]map[string]any, rule *[]string) {
|
||||
// var replacements = map[string]string{}
|
||||
// var selectArr []map[string]any
|
||||
// var urlTestArr []map[string]any
|
||||
// var fallbackArr []map[string]any
|
||||
// for _, group := range *proxyGroup {
|
||||
// switch group["type"] {
|
||||
// case "select":
|
||||
// selectArr = append(selectArr, group)
|
||||
// replacements[group["name"].(string)] = "Proxy"
|
||||
// break
|
||||
// case "url-test":
|
||||
// urlTestArr = append(urlTestArr, group)
|
||||
// replacements[group["name"].(string)] = "Auto"
|
||||
// break
|
||||
// case "fallback":
|
||||
// fallbackArr = append(fallbackArr, group)
|
||||
// replacements[group["name"].(string)] = "Fallback"
|
||||
// break
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ProxyProxies := Reduce(selectArr, []string{}, func(res []string, cur map[string]any) []string {
|
||||
// if cur["proxies"] == nil {
|
||||
// return res
|
||||
// }
|
||||
// for _, proxyName := range cur["proxies"].([]interface{}) {
|
||||
// if str, ok := proxyName.(string); ok {
|
||||
// str = replaceFromMap(str, replacements)
|
||||
// if str != "Proxy" {
|
||||
// res = append(res, str)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return res
|
||||
// })
|
||||
//
|
||||
// ProxyProxies = removeDuplicateFromSlice(ProxyProxies)
|
||||
//
|
||||
// AutoProxies := Reduce(urlTestArr, []string{}, func(res []string, cur map[string]any) []string {
|
||||
// if cur["proxies"] == nil {
|
||||
// return res
|
||||
// }
|
||||
// for _, proxyName := range cur["proxies"].([]interface{}) {
|
||||
// if str, ok := proxyName.(string); ok {
|
||||
// str = replaceFromMap(str, replacements)
|
||||
// if str != "Auto" {
|
||||
// res = append(res, str)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return res
|
||||
// })
|
||||
//
|
||||
// AutoProxies = removeDuplicateFromSlice(AutoProxies)
|
||||
//
|
||||
// FallbackProxies := Reduce(fallbackArr, []string{}, func(res []string, cur map[string]any) []string {
|
||||
// if cur["proxies"] == nil {
|
||||
// return res
|
||||
// }
|
||||
// for _, proxyName := range cur["proxies"].([]interface{}) {
|
||||
// if str, ok := proxyName.(string); ok {
|
||||
// str = replaceFromMap(str, replacements)
|
||||
// if str != "Fallback" {
|
||||
// res = append(res, str)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return res
|
||||
// })
|
||||
//
|
||||
// FallbackProxies = removeDuplicateFromSlice(FallbackProxies)
|
||||
//
|
||||
// var computedProxyGroup []map[string]any
|
||||
//
|
||||
// if len(ProxyProxies) > 0 {
|
||||
// computedProxyGroup = append(computedProxyGroup,
|
||||
// map[string]any{
|
||||
// "name": "Proxy",
|
||||
// "type": "select",
|
||||
// "proxies": ProxyProxies,
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// if len(AutoProxies) > 0 {
|
||||
// computedProxyGroup = append(computedProxyGroup,
|
||||
// map[string]any{
|
||||
// "name": "Auto",
|
||||
// "type": "url-test",
|
||||
// "proxies": AutoProxies,
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// if len(FallbackProxies) > 0 {
|
||||
// computedProxyGroup = append(computedProxyGroup,
|
||||
// map[string]any{
|
||||
// "name": "Fallback",
|
||||
// "type": "fallback",
|
||||
// "proxies": FallbackProxies,
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// computedRule := Map(*rule, func(value string) string {
|
||||
// return replaceFromMap(value, replacements)
|
||||
// })
|
||||
//
|
||||
// *proxyGroup = computedProxyGroup
|
||||
// *rule = computedRule
|
||||
//}
|
||||
|
||||
func genHosts(hosts, patchHosts map[string]any) {
|
||||
for k, v := range patchHosts {
|
||||
hosts[k] = v
|
||||
@@ -582,6 +438,5 @@ func applyConfig() error {
|
||||
patchSelectGroup()
|
||||
}
|
||||
updateListeners(cfg.General, cfg.Listeners)
|
||||
externalProviders = getExternalProvidersRaw()
|
||||
return err
|
||||
}
|
||||
|
||||
492
core/hub.go
492
core/hub.go
@@ -1,493 +1 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"context"
|
||||
bridge "core/dart-bridge"
|
||||
"core/state"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter"
|
||||
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
||||
"github.com/metacubex/mihomo/adapter/provider"
|
||||
"github.com/metacubex/mihomo/component/updater"
|
||||
"github.com/metacubex/mihomo/config"
|
||||
"github.com/metacubex/mihomo/constant"
|
||||
cp "github.com/metacubex/mihomo/constant/provider"
|
||||
"github.com/metacubex/mihomo/hub/executor"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/tunnel"
|
||||
"github.com/metacubex/mihomo/tunnel/statistic"
|
||||
)
|
||||
|
||||
var configParams = ConfigExtendedParams{}
|
||||
|
||||
var externalProviders = map[string]cp.Provider{}
|
||||
|
||||
var isInit = false
|
||||
|
||||
//export start
|
||||
func start() {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
isRunning = true
|
||||
}
|
||||
|
||||
//export stop
|
||||
func stop() {
|
||||
runLock.Lock()
|
||||
go func() {
|
||||
defer runLock.Unlock()
|
||||
isRunning = false
|
||||
stopListeners()
|
||||
}()
|
||||
}
|
||||
|
||||
//export initClash
|
||||
func initClash(homeDirStr *C.char) bool {
|
||||
if !isInit {
|
||||
constant.SetHomeDir(C.GoString(homeDirStr))
|
||||
isInit = true
|
||||
}
|
||||
return isInit
|
||||
}
|
||||
|
||||
//export getIsInit
|
||||
func getIsInit() bool {
|
||||
return isInit
|
||||
}
|
||||
|
||||
//export restartClash
|
||||
func restartClash() bool {
|
||||
execPath, _ := os.Executable()
|
||||
go restartExecutable(execPath)
|
||||
return true
|
||||
}
|
||||
|
||||
//export shutdownClash
|
||||
func shutdownClash() bool {
|
||||
stopListeners()
|
||||
executor.Shutdown()
|
||||
runtime.GC()
|
||||
isInit = false
|
||||
return true
|
||||
}
|
||||
|
||||
//export forceGc
|
||||
func forceGc() {
|
||||
go func() {
|
||||
log.Infoln("[APP] request force GC")
|
||||
runtime.GC()
|
||||
}()
|
||||
}
|
||||
|
||||
//export validateConfig
|
||||
func validateConfig(s *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
bytes := []byte(C.GoString(s))
|
||||
go func() {
|
||||
_, err := config.UnmarshalRawConfig(bytes)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
var updateLock sync.Mutex
|
||||
|
||||
//export updateConfig
|
||||
func updateConfig(s *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
paramsString := C.GoString(s)
|
||||
go func() {
|
||||
updateLock.Lock()
|
||||
defer updateLock.Unlock()
|
||||
var params = &GenerateConfigParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), params)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
configParams = params.Params
|
||||
prof := decorationConfig(params.ProfileId, params.Config)
|
||||
state.CurrentRawConfig = prof
|
||||
err = applyConfig()
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
//export clearEffect
|
||||
func clearEffect(s *C.char) {
|
||||
id := C.GoString(s)
|
||||
go func() {
|
||||
_ = removeFile(getProfilePath(id))
|
||||
_ = removeFile(getProfileProvidersPath(id))
|
||||
}()
|
||||
}
|
||||
|
||||
//export getProxies
|
||||
func getProxies() *C.char {
|
||||
data, err := json.Marshal(tunnel.ProxiesWithProviders())
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export changeProxy
|
||||
func changeProxy(s *C.char) {
|
||||
paramsString := C.GoString(s)
|
||||
var params = &ChangeProxyParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), params)
|
||||
if err != nil {
|
||||
log.Infoln("Unmarshal ChangeProxyParams %v", err)
|
||||
}
|
||||
groupName := *params.GroupName
|
||||
proxyName := *params.ProxyName
|
||||
proxies := tunnel.ProxiesWithProviders()
|
||||
group, ok := proxies[groupName]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
adapterProxy := group.(*adapter.Proxy)
|
||||
selector, ok := adapterProxy.ProxyAdapter.(outboundgroup.SelectAble)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if proxyName == "" {
|
||||
selector.ForceSet(proxyName)
|
||||
} else {
|
||||
err = selector.Set(proxyName)
|
||||
}
|
||||
if err == nil {
|
||||
log.Infoln("[SelectAble] %s selected %s", groupName, proxyName)
|
||||
}
|
||||
}
|
||||
|
||||
//export getTraffic
|
||||
func getTraffic() *C.char {
|
||||
up, down := statistic.DefaultManager.Current(state.CurrentState.OnlyProxy)
|
||||
traffic := map[string]int64{
|
||||
"up": up,
|
||||
"down": down,
|
||||
}
|
||||
data, err := json.Marshal(traffic)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export getTotalTraffic
|
||||
func getTotalTraffic() *C.char {
|
||||
up, down := statistic.DefaultManager.Total(state.CurrentState.OnlyProxy)
|
||||
traffic := map[string]int64{
|
||||
"up": up,
|
||||
"down": down,
|
||||
}
|
||||
data, err := json.Marshal(traffic)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export resetTraffic
|
||||
func resetTraffic() {
|
||||
statistic.DefaultManager.ResetStatistic()
|
||||
}
|
||||
|
||||
//export asyncTestDelay
|
||||
func asyncTestDelay(s *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
paramsString := C.GoString(s)
|
||||
b.Go(paramsString, func() (bool, error) {
|
||||
var params = &TestDelayParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), params)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, "")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
expectedStatus, err := utils.NewUnsignedRanges[uint16]("")
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, "")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(params.Timeout))
|
||||
defer cancel()
|
||||
|
||||
proxies := tunnel.ProxiesWithProviders()
|
||||
proxy := proxies[params.ProxyName]
|
||||
|
||||
delayData := &Delay{
|
||||
Name: params.ProxyName,
|
||||
}
|
||||
|
||||
if proxy == nil {
|
||||
delayData.Value = -1
|
||||
data, _ := json.Marshal(delayData)
|
||||
bridge.SendToPort(i, string(data))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus)
|
||||
if err != nil || delay == 0 {
|
||||
delayData.Value = -1
|
||||
data, _ := json.Marshal(delayData)
|
||||
bridge.SendToPort(i, string(data))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
delayData.Value = int32(delay)
|
||||
data, _ := json.Marshal(delayData)
|
||||
bridge.SendToPort(i, string(data))
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
//export getVersionInfo
|
||||
func getVersionInfo() *C.char {
|
||||
versionInfo := map[string]string{
|
||||
"clashName": constant.Name,
|
||||
"version": "1.18.5",
|
||||
}
|
||||
data, err := json.Marshal(versionInfo)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export getConnections
|
||||
func getConnections() *C.char {
|
||||
snapshot := statistic.DefaultManager.Snapshot()
|
||||
data, err := json.Marshal(snapshot)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export closeConnections
|
||||
func closeConnections() {
|
||||
statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
|
||||
err := c.Close()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
//export closeConnection
|
||||
func closeConnection(id *C.char) {
|
||||
connectionId := C.GoString(id)
|
||||
c := statistic.DefaultManager.Get(connectionId)
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
_ = c.Close()
|
||||
}
|
||||
|
||||
//export getProviders
|
||||
func getProviders() *C.char {
|
||||
data, err := json.Marshal(tunnel.Providers())
|
||||
var msg *C.char
|
||||
if err != nil {
|
||||
msg = C.CString("")
|
||||
return msg
|
||||
}
|
||||
msg = C.CString(string(data))
|
||||
return msg
|
||||
}
|
||||
|
||||
//export getProvider
|
||||
func getProvider(name *C.char) *C.char {
|
||||
providerName := C.GoString(name)
|
||||
providers := tunnel.Providers()
|
||||
data, err := json.Marshal(providers[providerName])
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export getExternalProviders
|
||||
func getExternalProviders() *C.char {
|
||||
eps := make([]ExternalProvider, 0)
|
||||
for _, p := range externalProviders {
|
||||
externalProvider, err := toExternalProvider(p)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
eps = append(eps, *externalProvider)
|
||||
}
|
||||
sort.Sort(ExternalProviders(eps))
|
||||
data, err := json.Marshal(eps)
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export getExternalProvider
|
||||
func getExternalProvider(name *C.char) *C.char {
|
||||
externalProviderName := C.GoString(name)
|
||||
externalProvider, exist := externalProviders[externalProviderName]
|
||||
if !exist {
|
||||
return C.CString("")
|
||||
}
|
||||
e, err := toExternalProvider(externalProvider)
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
data, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export updateGeoData
|
||||
func updateGeoData(geoType *C.char, geoName *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
geoTypeString := C.GoString(geoType)
|
||||
geoNameString := C.GoString(geoName)
|
||||
go func() {
|
||||
path := constant.Path.Resolve(geoNameString)
|
||||
switch geoTypeString {
|
||||
case "MMDB":
|
||||
err := updater.UpdateMMDBWithPath(path)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
case "ASN":
|
||||
err := updater.UpdateASNWithPath(path)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
case "GeoIp":
|
||||
err := updater.UpdateGeoIpWithPath(path)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
case "GeoSite":
|
||||
err := updater.UpdateGeoSiteWithPath(path)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
//export updateExternalProvider
|
||||
func updateExternalProvider(providerName *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
providerNameString := C.GoString(providerName)
|
||||
go func() {
|
||||
externalProvider, exist := externalProviders[providerNameString]
|
||||
if !exist {
|
||||
bridge.SendToPort(i, "external provider is not exist")
|
||||
return
|
||||
}
|
||||
err := externalProvider.Update()
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
//export sideLoadExternalProvider
|
||||
func sideLoadExternalProvider(providerName *C.char, data *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
bytes := []byte(C.GoString(data))
|
||||
providerNameString := C.GoString(providerName)
|
||||
go func() {
|
||||
externalProvider, exist := externalProviders[providerNameString]
|
||||
if !exist {
|
||||
bridge.SendToPort(i, "external provider is not exist")
|
||||
return
|
||||
}
|
||||
err := sideUpdateExternalProvider(externalProvider, bytes)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
//export initNativeApiBridge
|
||||
func initNativeApiBridge(api unsafe.Pointer) {
|
||||
bridge.InitDartApi(api)
|
||||
}
|
||||
|
||||
//export initMessage
|
||||
func initMessage(port C.longlong) {
|
||||
i := int64(port)
|
||||
Port = i
|
||||
}
|
||||
|
||||
//export freeCString
|
||||
func freeCString(s *C.char) {
|
||||
C.free(unsafe.Pointer(s))
|
||||
}
|
||||
|
||||
func init() {
|
||||
provider.HealthcheckHook = func(name string, delay uint16) {
|
||||
delayData := &Delay{
|
||||
Name: name,
|
||||
}
|
||||
if delay == 0 {
|
||||
delayData.Value = -1
|
||||
} else {
|
||||
delayData.Value = int32(delay)
|
||||
}
|
||||
SendMessage(Message{
|
||||
Type: DelayMessage,
|
||||
Data: delayData,
|
||||
})
|
||||
}
|
||||
statistic.DefaultRequestNotify = func(c statistic.Tracker) {
|
||||
SendMessage(Message{
|
||||
Type: RequestMessage,
|
||||
Data: c,
|
||||
})
|
||||
}
|
||||
executor.DefaultProviderLoadedHook = func(providerName string) {
|
||||
SendMessage(Message{
|
||||
Type: LoadedMessage,
|
||||
Data: providerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
475
core/lib.go
Normal file
475
core/lib.go
Normal file
@@ -0,0 +1,475 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"context"
|
||||
bridge "core/dart-bridge"
|
||||
"core/state"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter"
|
||||
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
||||
"github.com/metacubex/mihomo/adapter/provider"
|
||||
"github.com/metacubex/mihomo/component/updater"
|
||||
"github.com/metacubex/mihomo/config"
|
||||
"github.com/metacubex/mihomo/constant"
|
||||
cp "github.com/metacubex/mihomo/constant/provider"
|
||||
"github.com/metacubex/mihomo/hub/executor"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/tunnel"
|
||||
"github.com/metacubex/mihomo/tunnel/statistic"
|
||||
)
|
||||
|
||||
var configParams = ConfigExtendedParams{}
|
||||
|
||||
var externalProviders = map[string]cp.Provider{}
|
||||
|
||||
var isInit = false
|
||||
|
||||
//export start
|
||||
func start() {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
isRunning = true
|
||||
}
|
||||
|
||||
//export stop
|
||||
func stop() {
|
||||
runLock.Lock()
|
||||
go func() {
|
||||
defer runLock.Unlock()
|
||||
isRunning = false
|
||||
stopListeners()
|
||||
}()
|
||||
}
|
||||
|
||||
//export initClash
|
||||
func initClash(homeDirStr *C.char) bool {
|
||||
if !isInit {
|
||||
constant.SetHomeDir(C.GoString(homeDirStr))
|
||||
isInit = true
|
||||
}
|
||||
return isInit
|
||||
}
|
||||
|
||||
//export getIsInit
|
||||
func getIsInit() bool {
|
||||
return isInit
|
||||
}
|
||||
|
||||
//export restartClash
|
||||
func restartClash() bool {
|
||||
execPath, _ := os.Executable()
|
||||
go restartExecutable(execPath)
|
||||
return true
|
||||
}
|
||||
|
||||
//export shutdownClash
|
||||
func shutdownClash() bool {
|
||||
stopListeners()
|
||||
executor.Shutdown()
|
||||
runtime.GC()
|
||||
isInit = false
|
||||
return true
|
||||
}
|
||||
|
||||
//export forceGc
|
||||
func forceGc() {
|
||||
go func() {
|
||||
log.Infoln("[APP] request force GC")
|
||||
runtime.GC()
|
||||
}()
|
||||
}
|
||||
|
||||
//export validateConfig
|
||||
func validateConfig(s *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
bytes := []byte(C.GoString(s))
|
||||
go func() {
|
||||
_, err := config.UnmarshalRawConfig(bytes)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
var updateLock sync.Mutex
|
||||
|
||||
//export updateConfig
|
||||
func updateConfig(s *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
paramsString := C.GoString(s)
|
||||
go func() {
|
||||
updateLock.Lock()
|
||||
defer updateLock.Unlock()
|
||||
var params = &GenerateConfigParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), params)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
configParams = params.Params
|
||||
prof := decorationConfig(params.ProfileId, params.Config)
|
||||
state.CurrentRawConfig = prof
|
||||
err = applyConfig()
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
//export clearEffect
|
||||
func clearEffect(s *C.char) {
|
||||
id := C.GoString(s)
|
||||
go func() {
|
||||
_ = removeFile(getProfilePath(id))
|
||||
_ = removeFile(getProfileProvidersPath(id))
|
||||
}()
|
||||
}
|
||||
|
||||
//export getProxies
|
||||
func getProxies() *C.char {
|
||||
data, err := json.Marshal(tunnel.ProxiesWithProviders())
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export changeProxy
|
||||
func changeProxy(s *C.char) {
|
||||
paramsString := C.GoString(s)
|
||||
var params = &ChangeProxyParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), params)
|
||||
if err != nil {
|
||||
log.Infoln("Unmarshal ChangeProxyParams %v", err)
|
||||
}
|
||||
groupName := *params.GroupName
|
||||
proxyName := *params.ProxyName
|
||||
proxies := tunnel.ProxiesWithProviders()
|
||||
group, ok := proxies[groupName]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
adapterProxy := group.(*adapter.Proxy)
|
||||
selector, ok := adapterProxy.ProxyAdapter.(outboundgroup.SelectAble)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if proxyName == "" {
|
||||
selector.ForceSet(proxyName)
|
||||
} else {
|
||||
err = selector.Set(proxyName)
|
||||
}
|
||||
if err == nil {
|
||||
log.Infoln("[SelectAble] %s selected %s", groupName, proxyName)
|
||||
}
|
||||
}
|
||||
|
||||
//export getTraffic
|
||||
func getTraffic() *C.char {
|
||||
up, down := statistic.DefaultManager.Current(state.CurrentState.OnlyProxy)
|
||||
traffic := map[string]int64{
|
||||
"up": up,
|
||||
"down": down,
|
||||
}
|
||||
data, err := json.Marshal(traffic)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export getTotalTraffic
|
||||
func getTotalTraffic() *C.char {
|
||||
up, down := statistic.DefaultManager.Total(state.CurrentState.OnlyProxy)
|
||||
traffic := map[string]int64{
|
||||
"up": up,
|
||||
"down": down,
|
||||
}
|
||||
data, err := json.Marshal(traffic)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export resetTraffic
|
||||
func resetTraffic() {
|
||||
statistic.DefaultManager.ResetStatistic()
|
||||
}
|
||||
|
||||
//export asyncTestDelay
|
||||
func asyncTestDelay(s *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
paramsString := C.GoString(s)
|
||||
b.Go(paramsString, func() (bool, error) {
|
||||
var params = &TestDelayParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), params)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, "")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
expectedStatus, err := utils.NewUnsignedRanges[uint16]("")
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, "")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(params.Timeout))
|
||||
defer cancel()
|
||||
|
||||
proxies := tunnel.ProxiesWithProviders()
|
||||
proxy := proxies[params.ProxyName]
|
||||
|
||||
delayData := &Delay{
|
||||
Name: params.ProxyName,
|
||||
}
|
||||
|
||||
if proxy == nil {
|
||||
delayData.Value = -1
|
||||
data, _ := json.Marshal(delayData)
|
||||
bridge.SendToPort(i, string(data))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus)
|
||||
if err != nil || delay == 0 {
|
||||
delayData.Value = -1
|
||||
data, _ := json.Marshal(delayData)
|
||||
bridge.SendToPort(i, string(data))
|
||||
return false, nil
|
||||
}
|
||||
|
||||
delayData.Value = int32(delay)
|
||||
data, _ := json.Marshal(delayData)
|
||||
bridge.SendToPort(i, string(data))
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
//export getVersionInfo
|
||||
func getVersionInfo() *C.char {
|
||||
versionInfo := map[string]string{
|
||||
"clashName": constant.Name,
|
||||
"version": "1.18.5",
|
||||
}
|
||||
data, err := json.Marshal(versionInfo)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export getConnections
|
||||
func getConnections() *C.char {
|
||||
snapshot := statistic.DefaultManager.Snapshot()
|
||||
data, err := json.Marshal(snapshot)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export closeConnections
|
||||
func closeConnections() {
|
||||
statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
|
||||
err := c.Close()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
//export closeConnection
|
||||
func closeConnection(id *C.char) {
|
||||
connectionId := C.GoString(id)
|
||||
c := statistic.DefaultManager.Get(connectionId)
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
_ = c.Close()
|
||||
}
|
||||
|
||||
//export getExternalProviders
|
||||
func getExternalProviders() *C.char {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
externalProviders = getExternalProvidersRaw()
|
||||
eps := make([]ExternalProvider, 0)
|
||||
for _, p := range externalProviders {
|
||||
externalProvider, err := toExternalProvider(p)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
eps = append(eps, *externalProvider)
|
||||
}
|
||||
sort.Sort(ExternalProviders(eps))
|
||||
data, err := json.Marshal(eps)
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export getExternalProvider
|
||||
func getExternalProvider(name *C.char) *C.char {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
externalProviderName := C.GoString(name)
|
||||
externalProvider, exist := externalProviders[externalProviderName]
|
||||
if !exist {
|
||||
return C.CString("")
|
||||
}
|
||||
e, err := toExternalProvider(externalProvider)
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
data, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export updateGeoData
|
||||
func updateGeoData(geoType *C.char, geoName *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
geoTypeString := C.GoString(geoType)
|
||||
geoNameString := C.GoString(geoName)
|
||||
go func() {
|
||||
path := constant.Path.Resolve(geoNameString)
|
||||
switch geoTypeString {
|
||||
case "MMDB":
|
||||
err := updater.UpdateMMDBWithPath(path)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
case "ASN":
|
||||
err := updater.UpdateASNWithPath(path)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
case "GeoIp":
|
||||
err := updater.UpdateGeoIpWithPath(path)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
case "GeoSite":
|
||||
err := updater.UpdateGeoSiteWithPath(path)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
//export updateExternalProvider
|
||||
func updateExternalProvider(providerName *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
providerNameString := C.GoString(providerName)
|
||||
go func() {
|
||||
externalProvider, exist := externalProviders[providerNameString]
|
||||
if !exist {
|
||||
bridge.SendToPort(i, "external provider is not exist")
|
||||
return
|
||||
}
|
||||
err := externalProvider.Update()
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
//export sideLoadExternalProvider
|
||||
func sideLoadExternalProvider(providerName *C.char, data *C.char, port C.longlong) {
|
||||
i := int64(port)
|
||||
bytes := []byte(C.GoString(data))
|
||||
providerNameString := C.GoString(providerName)
|
||||
go func() {
|
||||
externalProvider, exist := externalProviders[providerNameString]
|
||||
if !exist {
|
||||
bridge.SendToPort(i, "external provider is not exist")
|
||||
return
|
||||
}
|
||||
err := sideUpdateExternalProvider(externalProvider, bytes)
|
||||
if err != nil {
|
||||
bridge.SendToPort(i, err.Error())
|
||||
return
|
||||
}
|
||||
bridge.SendToPort(i, "")
|
||||
}()
|
||||
}
|
||||
|
||||
//export initNativeApiBridge
|
||||
func initNativeApiBridge(api unsafe.Pointer) {
|
||||
bridge.InitDartApi(api)
|
||||
}
|
||||
|
||||
//export initMessage
|
||||
func initMessage(port C.longlong) {
|
||||
i := int64(port)
|
||||
Port = i
|
||||
}
|
||||
|
||||
//export freeCString
|
||||
func freeCString(s *C.char) {
|
||||
C.free(unsafe.Pointer(s))
|
||||
}
|
||||
|
||||
func init() {
|
||||
provider.HealthcheckHook = func(name string, delay uint16) {
|
||||
delayData := &Delay{
|
||||
Name: name,
|
||||
}
|
||||
if delay == 0 {
|
||||
delayData.Value = -1
|
||||
} else {
|
||||
delayData.Value = int32(delay)
|
||||
}
|
||||
SendMessage(Message{
|
||||
Type: DelayMessage,
|
||||
Data: delayData,
|
||||
})
|
||||
}
|
||||
statistic.DefaultRequestNotify = func(c statistic.Tracker) {
|
||||
SendMessage(Message{
|
||||
Type: RequestMessage,
|
||||
Data: c,
|
||||
})
|
||||
}
|
||||
executor.DefaultProviderLoadedHook = func(providerName string) {
|
||||
SendMessage(Message{
|
||||
Type: LoadedMessage,
|
||||
Data: providerName,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ func getAndroidVpnOptions() *C.char {
|
||||
AccessControl: state.CurrentState.AccessControl,
|
||||
SystemProxy: state.CurrentState.SystemProxy,
|
||||
AllowBypass: state.CurrentState.AllowBypass,
|
||||
RouteAddress: state.CurrentState.RouteAddress,
|
||||
BypassDomain: state.CurrentState.BypassDomain,
|
||||
DnsServerAddress: state.GetDnsServerAddress(),
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ type AndroidVpnOptions struct {
|
||||
AllowBypass bool `json:"allowBypass"`
|
||||
SystemProxy bool `json:"systemProxy"`
|
||||
BypassDomain []string `json:"bypassDomain"`
|
||||
RouteAddress []string `json:"routeAddress"`
|
||||
Ipv4Address string `json:"ipv4Address"`
|
||||
Ipv6Address string `json:"ipv6Address"`
|
||||
DnsServerAddress string `json:"dnsServerAddress"`
|
||||
@@ -32,6 +33,7 @@ type AndroidVpnRawOptions struct {
|
||||
AccessControl *AccessControl `json:"accessControl"`
|
||||
AllowBypass bool `json:"allowBypass"`
|
||||
SystemProxy bool `json:"systemProxy"`
|
||||
RouteAddress []string `json:"routeAddress"`
|
||||
Ipv6 bool `json:"ipv6"`
|
||||
BypassDomain []string `json:"bypassDomain"`
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ func initSocketHook() {
|
||||
}
|
||||
return conn.Control(func(fd uintptr) {
|
||||
fdInt := int64(fd)
|
||||
timeout := time.After(100 * time.Millisecond)
|
||||
timeout := time.After(500 * time.Millisecond)
|
||||
id := atomic.AddInt64(&fdCounter, 1)
|
||||
|
||||
markSocket(Fd{
|
||||
@@ -145,7 +145,7 @@ func initSocketHook() {
|
||||
if exists {
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/l10n/l10n.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/manager/hotkey_manager.dart';
|
||||
import 'package:fl_clash/manager/manager.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
@@ -17,6 +19,7 @@ runAppWithPreferences(
|
||||
Widget child, {
|
||||
required AppState appState,
|
||||
required Config config,
|
||||
required AppFlowingState appFlowingState,
|
||||
required ClashConfig clashConfig,
|
||||
}) {
|
||||
runApp(MultiProvider(
|
||||
@@ -28,7 +31,7 @@ runAppWithPreferences(
|
||||
create: (_) => config,
|
||||
),
|
||||
ChangeNotifierProvider<AppFlowingState>(
|
||||
create: (_) => AppFlowingState(),
|
||||
create: (_) => appFlowingState,
|
||||
),
|
||||
ChangeNotifierProxyProvider2<Config, ClashConfig, AppState>(
|
||||
create: (_) => appState,
|
||||
@@ -58,10 +61,18 @@ class ApplicationState extends State<Application> {
|
||||
|
||||
final _pageTransitionsTheme = const PageTransitionsTheme(
|
||||
builders: <TargetPlatform, PageTransitionsBuilder>{
|
||||
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.windows: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.linux: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.android: SharedAxisPageTransitionsBuilder(
|
||||
transitionType: SharedAxisTransitionType.horizontal,
|
||||
),
|
||||
TargetPlatform.windows: SharedAxisPageTransitionsBuilder(
|
||||
transitionType: SharedAxisTransitionType.horizontal,
|
||||
),
|
||||
TargetPlatform.linux: SharedAxisPageTransitionsBuilder(
|
||||
transitionType: SharedAxisTransitionType.horizontal,
|
||||
),
|
||||
TargetPlatform.macOS: SharedAxisPageTransitionsBuilder(
|
||||
transitionType: SharedAxisTransitionType.horizontal,
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -93,6 +104,7 @@ class ApplicationState extends State<Application> {
|
||||
}
|
||||
await globalState.appController.init();
|
||||
globalState.appController.initLink();
|
||||
app?.initShortcuts();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:ffi/ffi.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
|
||||
import 'generated/clash_ffi.dart';
|
||||
|
||||
class ClashCore {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,6 @@ extension ColorSchemeExtension on ColorScheme {
|
||||
ColorScheme toPrueBlack(bool isPrueBlack) => isPrueBlack
|
||||
? copyWith(
|
||||
surface: Colors.black,
|
||||
background: Colors.black,
|
||||
surfaceContainer: surfaceContainer.darken(0.05),
|
||||
)
|
||||
: this;
|
||||
|
||||
@@ -29,4 +29,5 @@ export 'scroll.dart';
|
||||
export 'icons.dart';
|
||||
export 'http.dart';
|
||||
export 'keyboard.dart';
|
||||
export 'network.dart';
|
||||
export 'network.dart';
|
||||
export 'navigator.dart';
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app_links/app_links.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef InstallConfigCallBack = void Function(String url);
|
||||
|
||||
@@ -17,7 +17,7 @@ class LinkManager {
|
||||
initAppLinksListen(installConfigCallBack) async {
|
||||
debugPrint("initAppLinksListen");
|
||||
destroy();
|
||||
subscription = _appLinks.allUriLinkStream.listen(
|
||||
subscription = _appLinks.uriLinkStream.listen(
|
||||
(uri) {
|
||||
debugPrint('onAppLink: $uri');
|
||||
if (uri.host == 'install-config') {
|
||||
@@ -31,8 +31,7 @@ class LinkManager {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
destroy(){
|
||||
destroy() {
|
||||
if (subscription != null) {
|
||||
subscription?.cancel();
|
||||
subscription = null;
|
||||
|
||||
11
lib/common/navigator.dart
Normal file
11
lib/common/navigator.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BaseNavigator {
|
||||
static Future<T?> push<T>(BuildContext context, Widget child) async {
|
||||
return await Navigator.of(context).push<T>(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
@@ -101,14 +102,13 @@ class Other {
|
||||
}
|
||||
|
||||
String getTrayIconPath({
|
||||
required bool isStart,
|
||||
required Brightness brightness,
|
||||
}) {
|
||||
if(Platform.isMacOS){
|
||||
return "assets/images/icon_white.png";
|
||||
}
|
||||
final suffix = Platform.isWindows ? "ico" : "png";
|
||||
if (isStart && Platform.isWindows) {
|
||||
if (Platform.isWindows) {
|
||||
return "assets/images/icon.$suffix";
|
||||
}
|
||||
return switch (brightness) {
|
||||
@@ -188,10 +188,8 @@ class Other {
|
||||
return parameters[fileNameKey];
|
||||
}
|
||||
|
||||
double getViewWidth() {
|
||||
final view = WidgetsBinding.instance.platformDispatcher.views.first;
|
||||
final size = view.physicalSize / view.devicePixelRatio;
|
||||
return size.width;
|
||||
FlutterView getScreen() {
|
||||
return WidgetsBinding.instance.platformDispatcher.views.first;
|
||||
}
|
||||
|
||||
List<String> parseReleaseBody(String? body) {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/config.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:windows_single_instance/windows_single_instance.dart';
|
||||
|
||||
import 'protocol.dart';
|
||||
import 'system.dart';
|
||||
|
||||
class Window {
|
||||
init(WindowProps props, int version) async {
|
||||
if (Platform.isWindows) {
|
||||
@@ -21,14 +19,7 @@ class Window {
|
||||
size: Size(props.width, props.height),
|
||||
minimumSize: const Size(380, 500),
|
||||
);
|
||||
if (props.left != null || props.top != null) {
|
||||
await windowManager.setPosition(
|
||||
Offset(props.left ?? 0, props.top ?? 0),
|
||||
);
|
||||
} else {
|
||||
await windowManager.setAlignment(Alignment.center);
|
||||
}
|
||||
if(!Platform.isMacOS || version > 10){
|
||||
if (!Platform.isMacOS || version > 10) {
|
||||
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
|
||||
}
|
||||
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
|
||||
@@ -9,15 +9,13 @@ import 'package:fl_clash/common/archive.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'clash/core.dart';
|
||||
import 'models/models.dart';
|
||||
import 'common/common.dart';
|
||||
import 'models/models.dart';
|
||||
|
||||
class AppController {
|
||||
final BuildContext context;
|
||||
@@ -29,6 +27,7 @@ class AppController {
|
||||
late Function updateGroupDebounce;
|
||||
late Function addCheckIpNumDebounce;
|
||||
late Function applyProfileDebounce;
|
||||
late Function savePreferencesDebounce;
|
||||
|
||||
AppController(this.context) {
|
||||
appState = context.read<AppState>();
|
||||
@@ -38,6 +37,9 @@ class AppController {
|
||||
updateClashConfigDebounce = debounce<Function()>(() async {
|
||||
await updateClashConfig();
|
||||
});
|
||||
savePreferencesDebounce = debounce<Function()>(() async {
|
||||
await savePreferences();
|
||||
});
|
||||
applyProfileDebounce = debounce<Function()>(() async {
|
||||
await applyProfile(isPrue: true);
|
||||
});
|
||||
@@ -51,10 +53,7 @@ class AppController {
|
||||
|
||||
updateStatus(bool isStart) async {
|
||||
if (isStart) {
|
||||
await globalState.handleStart(
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
await globalState.handleStart();
|
||||
updateRunTime();
|
||||
updateTraffic();
|
||||
globalState.updateFunctionLists = [
|
||||
@@ -115,6 +114,10 @@ class AppController {
|
||||
}
|
||||
}
|
||||
|
||||
updateProviders() {
|
||||
globalState.updateProviders(appState);
|
||||
}
|
||||
|
||||
Future<void> updateProfile(Profile profile) async {
|
||||
final newProfile = await profile.update();
|
||||
config.setProfile(
|
||||
@@ -202,17 +205,8 @@ class AppController {
|
||||
}
|
||||
|
||||
savePreferences() async {
|
||||
await saveConfigPreferences();
|
||||
await saveClashConfigPreferences();
|
||||
}
|
||||
|
||||
saveConfigPreferences() async {
|
||||
debugPrint("saveConfigPreferences");
|
||||
debugPrint("[APP] savePreferences");
|
||||
await preferences.saveConfig(config);
|
||||
}
|
||||
|
||||
saveClashConfigPreferences() async {
|
||||
debugPrint("saveClashConfigPreferences");
|
||||
await preferences.saveClashConfig(clashConfig);
|
||||
}
|
||||
|
||||
@@ -231,7 +225,7 @@ class AppController {
|
||||
handleBackOrExit() async {
|
||||
if (config.appSetting.minimizeOnExit) {
|
||||
if (system.isDesktop) {
|
||||
await savePreferences();
|
||||
await savePreferencesDebounce();
|
||||
}
|
||||
await system.back();
|
||||
} else {
|
||||
@@ -538,8 +532,8 @@ class AppController {
|
||||
}
|
||||
|
||||
updateSystemProxy() {
|
||||
config.desktopProps = config.desktopProps.copyWith(
|
||||
systemProxy: !config.desktopProps.systemProxy,
|
||||
config.networkProps = config.networkProps.copyWith(
|
||||
systemProxy: !config.networkProps.systemProxy,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -607,122 +601,14 @@ class AppController {
|
||||
});
|
||||
}
|
||||
|
||||
Future _updateSystemTray({
|
||||
required bool isStart,
|
||||
required Brightness? brightness,
|
||||
bool force = false,
|
||||
}) async {
|
||||
if (Platform.isLinux || force) {
|
||||
await trayManager.destroy();
|
||||
}
|
||||
await trayManager.setIcon(
|
||||
other.getTrayIconPath(
|
||||
isStart: isStart,
|
||||
brightness: brightness ??
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness,
|
||||
),
|
||||
isTemplate: true,
|
||||
);
|
||||
if (!Platform.isLinux) {
|
||||
await trayManager.setToolTip(
|
||||
appName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
updateTray([bool focus = false]) async {
|
||||
if (!Platform.isLinux) {
|
||||
await _updateSystemTray(
|
||||
isStart: appFlowingState.isStart,
|
||||
brightness: appState.brightness,
|
||||
force: focus,
|
||||
);
|
||||
}
|
||||
List<MenuItem> menuItems = [];
|
||||
final showMenuItem = MenuItem(
|
||||
label: appLocalizations.show,
|
||||
onClick: (_) {
|
||||
window?.show();
|
||||
},
|
||||
globalState.updateTray(
|
||||
appState: appState,
|
||||
appFlowingState: appFlowingState,
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
focus: focus,
|
||||
);
|
||||
menuItems.add(showMenuItem);
|
||||
final startMenuItem = MenuItem.checkbox(
|
||||
label: appFlowingState.isStart
|
||||
? appLocalizations.stop
|
||||
: appLocalizations.start,
|
||||
onClick: (_) async {
|
||||
globalState.appController.updateStart();
|
||||
},
|
||||
checked: false,
|
||||
);
|
||||
menuItems.add(startMenuItem);
|
||||
menuItems.add(MenuItem.separator());
|
||||
for (final mode in Mode.values) {
|
||||
menuItems.add(
|
||||
MenuItem.checkbox(
|
||||
label: Intl.message(mode.name),
|
||||
onClick: (_) {
|
||||
globalState.appController.clashConfig.mode = mode;
|
||||
},
|
||||
checked: mode == clashConfig.mode,
|
||||
),
|
||||
);
|
||||
}
|
||||
menuItems.add(MenuItem.separator());
|
||||
if (appFlowingState.isStart) {
|
||||
menuItems.add(
|
||||
MenuItem.checkbox(
|
||||
label: appLocalizations.tun,
|
||||
onClick: (_) {
|
||||
globalState.appController.updateTun();
|
||||
},
|
||||
checked: clashConfig.tun.enable,
|
||||
),
|
||||
);
|
||||
menuItems.add(
|
||||
MenuItem.checkbox(
|
||||
label: appLocalizations.systemProxy,
|
||||
onClick: (_) {
|
||||
globalState.appController.updateSystemProxy();
|
||||
},
|
||||
checked: config.desktopProps.systemProxy,
|
||||
),
|
||||
);
|
||||
menuItems.add(MenuItem.separator());
|
||||
}
|
||||
final autoStartMenuItem = MenuItem.checkbox(
|
||||
label: appLocalizations.autoLaunch,
|
||||
onClick: (_) async {
|
||||
globalState.appController.updateAutoLaunch();
|
||||
},
|
||||
checked: config.appSetting.autoLaunch,
|
||||
);
|
||||
final adminAutoStartMenuItem = MenuItem.checkbox(
|
||||
label: appLocalizations.adminAutoLaunch,
|
||||
onClick: (_) async {
|
||||
globalState.appController.updateAdminAutoLaunch();
|
||||
},
|
||||
checked: config.appSetting.adminAutoLaunch,
|
||||
);
|
||||
menuItems.add(autoStartMenuItem);
|
||||
menuItems.add(adminAutoStartMenuItem);
|
||||
menuItems.add(MenuItem.separator());
|
||||
final exitMenuItem = MenuItem(
|
||||
label: appLocalizations.exit,
|
||||
onClick: (_) async {
|
||||
await globalState.appController.handleExit();
|
||||
},
|
||||
);
|
||||
menuItems.add(exitMenuItem);
|
||||
final menu = Menu(items: menuItems);
|
||||
await trayManager.setContextMenu(menu);
|
||||
if (Platform.isLinux) {
|
||||
await _updateSystemTray(
|
||||
isStart: appFlowingState.isStart,
|
||||
brightness: appState.brightness,
|
||||
force: focus,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
recoveryData(
|
||||
|
||||
@@ -173,3 +173,8 @@ enum FontFamily {
|
||||
|
||||
const FontFamily([this.value]);
|
||||
}
|
||||
|
||||
enum RouteMode {
|
||||
bypassPrivate,
|
||||
config,
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
title: appLocalizations.network,
|
||||
isScaffold: true,
|
||||
isBlur: false,
|
||||
extendPageWidth: 360,
|
||||
widget: const NetworkListView(),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -6,10 +6,11 @@ import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class VPNSwitch extends StatelessWidget {
|
||||
const VPNSwitch({super.key});
|
||||
class VPNItem extends StatelessWidget {
|
||||
const VPNItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -39,8 +40,8 @@ class TUNItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<Config, bool>(
|
||||
selector: (_, config) => config.vpnProps.enable,
|
||||
return Selector<ClashConfig, bool>(
|
||||
selector: (_, clashConfig) => clashConfig.tun.enable,
|
||||
builder: (_, enable, __) {
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.tun),
|
||||
@@ -60,8 +61,8 @@ class TUNItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class AllowBypassSwitch extends StatelessWidget {
|
||||
const AllowBypassSwitch({super.key});
|
||||
class AllowBypassItem extends StatelessWidget {
|
||||
const AllowBypassItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -87,8 +88,8 @@ class AllowBypassSwitch extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class SystemProxySwitch extends StatelessWidget {
|
||||
const SystemProxySwitch({super.key});
|
||||
class VpnSystemProxyItem extends StatelessWidget {
|
||||
const VpnSystemProxyItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -114,8 +115,35 @@ class SystemProxySwitch extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class Ipv6Switch extends StatelessWidget {
|
||||
const Ipv6Switch({super.key});
|
||||
class SystemProxyItem extends StatelessWidget {
|
||||
const SystemProxyItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<Config, bool>(
|
||||
selector: (_, config) => config.networkProps.systemProxy,
|
||||
builder: (_, systemProxy, __) {
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.systemProxy),
|
||||
subtitle: Text(appLocalizations.systemProxyDesc),
|
||||
delegate: SwitchDelegate(
|
||||
value: systemProxy,
|
||||
onChanged: (bool value) async {
|
||||
final config = globalState.appController.config;
|
||||
final networkProps = config.networkProps;
|
||||
config.networkProps = networkProps.copyWith(
|
||||
systemProxy: value,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Ipv6Item extends StatelessWidget {
|
||||
const Ipv6Item({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -176,6 +204,36 @@ class TunStackItem extends StatelessWidget {
|
||||
class BypassDomainItem extends StatelessWidget {
|
||||
const BypassDomainItem({super.key});
|
||||
|
||||
_initActions(BuildContext context) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
final commonScaffoldState =
|
||||
context.findAncestorStateOfType<CommonScaffoldState>();
|
||||
commonScaffoldState?.actions = [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
globalState.showMessage(
|
||||
title: appLocalizations.reset,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.resetTip,
|
||||
),
|
||||
onTab: () {
|
||||
final config = globalState.appController.config;
|
||||
config.networkProps = config.networkProps.copyWith(
|
||||
bypassDomain: defaultBypassDomain,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
tooltip: appLocalizations.reset,
|
||||
icon: const Icon(
|
||||
Icons.replay,
|
||||
),
|
||||
)
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListItem.open(
|
||||
@@ -183,19 +241,20 @@ class BypassDomainItem extends StatelessWidget {
|
||||
subtitle: Text(appLocalizations.bypassDomainDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
isScaffold: true,
|
||||
title: appLocalizations.bypassDomain,
|
||||
widget: Selector<Config, List<String>>(
|
||||
selector: (_, config) => config.vpnProps.bypassDomain,
|
||||
shouldRebuild: (prev, next) =>
|
||||
!stringListEquality.equals(prev, next),
|
||||
builder: (_, bypassDomain, __) {
|
||||
selector: (_, config) => config.networkProps.bypassDomain,
|
||||
shouldRebuild: (prev, next) => !stringListEquality.equals(prev, next),
|
||||
builder: (context, bypassDomain, __) {
|
||||
_initActions(context);
|
||||
return ListPage(
|
||||
title: appLocalizations.bypassDomain,
|
||||
items: bypassDomain,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items){
|
||||
onChange: (items) {
|
||||
final config = globalState.appController.config;
|
||||
config.vpnProps = config.vpnProps.copyWith(
|
||||
config.networkProps = config.networkProps.copyWith(
|
||||
bypassDomain: List.from(items),
|
||||
);
|
||||
},
|
||||
@@ -208,22 +267,108 @@ class BypassDomainItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class RouteModeItem extends StatelessWidget {
|
||||
const RouteModeItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<ClashConfig, RouteMode>(
|
||||
selector: (_, clashConfig) => clashConfig.routeMode,
|
||||
builder: (_, value, __) {
|
||||
return ListItem<RouteMode>.options(
|
||||
title: Text(appLocalizations.routeMode),
|
||||
subtitle: Text(Intl.message("routeMode_${value.name}")),
|
||||
delegate: OptionsDelegate<RouteMode>(
|
||||
title: appLocalizations.routeMode,
|
||||
options: RouteMode.values,
|
||||
onChanged: (RouteMode? value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
final appController = globalState.appController;
|
||||
appController.clashConfig.routeMode = value;
|
||||
},
|
||||
textBuilder: (routeMode) => Intl.message(
|
||||
"routeMode_${routeMode.name}",
|
||||
),
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RouteAddressItem extends StatelessWidget {
|
||||
const RouteAddressItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<ClashConfig, bool>(
|
||||
selector: (_, clashConfig) => clashConfig.routeMode == RouteMode.config,
|
||||
builder: (_, value, child) {
|
||||
if (value) {
|
||||
return child!;
|
||||
}
|
||||
return Container();
|
||||
},
|
||||
child: ListItem.open(
|
||||
title: Text(appLocalizations.routeAddress),
|
||||
subtitle: Text(appLocalizations.routeAddressDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
isScaffold: true,
|
||||
title: appLocalizations.routeAddress,
|
||||
widget: Selector<ClashConfig, List<String>>(
|
||||
selector: (_, clashConfig) => clashConfig.includeRouteAddress,
|
||||
shouldRebuild: (prev, next) =>
|
||||
!stringListEquality.equals(prev, next),
|
||||
builder: (context, routeAddress, __) {
|
||||
return ListPage(
|
||||
title: appLocalizations.routeAddress,
|
||||
items: routeAddress,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
final clashConfig = globalState.appController.clashConfig;
|
||||
clashConfig.includeRouteAddress =
|
||||
Set<String>.from(items).toList();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final networkItems = [
|
||||
Platform.isAndroid ? const VPNSwitch() : const TUNItem(),
|
||||
if (Platform.isAndroid) const VPNItem(),
|
||||
if (Platform.isAndroid)
|
||||
...generateSection(
|
||||
title: "VPN",
|
||||
items: [
|
||||
const SystemProxySwitch(),
|
||||
const AllowBypassSwitch(),
|
||||
const Ipv6Switch(),
|
||||
const BypassDomainItem(),
|
||||
const SystemProxyItem(),
|
||||
const AllowBypassItem(),
|
||||
const Ipv6Item(),
|
||||
],
|
||||
),
|
||||
if (system.isDesktop)
|
||||
...generateSection(
|
||||
title: appLocalizations.system,
|
||||
items: [
|
||||
SystemProxyItem(),
|
||||
BypassDomainItem(),
|
||||
],
|
||||
),
|
||||
...generateSection(
|
||||
title: appLocalizations.options,
|
||||
items: [
|
||||
if (system.isDesktop) const TUNItem(),
|
||||
const TunStackItem(),
|
||||
const RouteModeItem(),
|
||||
const RouteAddressItem(),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/fragments/dashboard/intranet_ip.dart';
|
||||
import 'package:fl_clash/fragments/dashboard/status_switch.dart';
|
||||
import 'package:fl_clash/fragments/dashboard/status_button.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'network_detection.dart';
|
||||
import 'network_speed.dart';
|
||||
import 'outbound_mode.dart';
|
||||
import 'start_button.dart';
|
||||
import 'network_speed.dart';
|
||||
import 'traffic_usage.dart';
|
||||
|
||||
class DashboardFragment extends StatefulWidget {
|
||||
@@ -21,12 +23,25 @@ class DashboardFragment extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _DashboardFragmentState extends State<DashboardFragment> {
|
||||
_initFab(bool isCurrent) {
|
||||
if (!isCurrent) {
|
||||
return;
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final commonScaffoldState =
|
||||
context.findAncestorStateOfType<CommonScaffoldState>();
|
||||
commonScaffoldState?.floatingActionButton = const StartButton();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FloatLayout(
|
||||
floatingWidget: const FloatWrapper(
|
||||
child: StartButton(),
|
||||
),
|
||||
return ActiveBuilder(
|
||||
label: "dashboard",
|
||||
builder: (isCurrent, child) {
|
||||
_initFab(isCurrent);
|
||||
return child!;
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SingleChildScrollView(
|
||||
@@ -53,13 +68,14 @@ class _DashboardFragmentState extends State<DashboardFragment> {
|
||||
// child: const VPNSwitch(),
|
||||
// ),
|
||||
if (system.isDesktop) ...[
|
||||
if (Platform.isWindows)
|
||||
GridItem(
|
||||
crossAxisCellCount: switchCount,
|
||||
child: const TUNButton(),
|
||||
),
|
||||
GridItem(
|
||||
crossAxisCellCount: switchCount,
|
||||
child: const TUNSwitch(),
|
||||
),
|
||||
GridItem(
|
||||
crossAxisCellCount: switchCount,
|
||||
child: const ProxySwitch(),
|
||||
child: const SystemProxyButton(),
|
||||
),
|
||||
],
|
||||
const GridItem(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
@@ -14,14 +16,14 @@ class IntranetIP extends StatefulWidget {
|
||||
|
||||
class _IntranetIPState extends State<IntranetIP> {
|
||||
final ipNotifier = ValueNotifier<String?>("");
|
||||
late StreamSubscription subscription;
|
||||
|
||||
Future<String> getNetworkType() async {
|
||||
try {
|
||||
List<NetworkInterface> interfaces = await NetworkInterface.list(
|
||||
final interfaces = await NetworkInterface.list(
|
||||
includeLoopback: false,
|
||||
type: InternetAddressType.any,
|
||||
);
|
||||
|
||||
for (var interface in interfaces) {
|
||||
if (interface.name.toLowerCase().contains('wlan') ||
|
||||
interface.name.toLowerCase().contains('wi-fi')) {
|
||||
@@ -33,7 +35,6 @@ class _IntranetIPState extends State<IntranetIP> {
|
||||
return 'Mobile Data';
|
||||
}
|
||||
}
|
||||
|
||||
return 'Unknown';
|
||||
} catch (e) {
|
||||
return 'Error';
|
||||
@@ -41,6 +42,7 @@ class _IntranetIPState extends State<IntranetIP> {
|
||||
}
|
||||
|
||||
Future<String?> getLocalIpAddress() async {
|
||||
await Future.delayed(animateDuration);
|
||||
List<NetworkInterface> interfaces = await NetworkInterface.list(
|
||||
includeLoopback: false,
|
||||
)
|
||||
@@ -66,15 +68,14 @@ class _IntranetIPState extends State<IntranetIP> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
ipNotifier.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
subscription = Connectivity().onConnectivityChanged.listen((_) async {
|
||||
ipNotifier.value = null;
|
||||
debugPrint("[App] Connection change");
|
||||
ipNotifier.value = await getLocalIpAddress() ?? "";
|
||||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
ipNotifier.value = await getLocalIpAddress() ?? "";
|
||||
});
|
||||
@@ -104,7 +105,9 @@ class _IntranetIPState extends State<IntranetIP> {
|
||||
flex: 1,
|
||||
child: TooltipText(
|
||||
text: Text(
|
||||
value.isNotEmpty ? value : appLocalizations.noNetwork,
|
||||
value.isNotEmpty
|
||||
? value
|
||||
: appLocalizations.noNetwork,
|
||||
style: context
|
||||
.textTheme.titleLarge?.toSoftBold.toMinus,
|
||||
maxLines: 1,
|
||||
@@ -127,4 +130,11 @@ class _IntranetIPState extends State<IntranetIP> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
subscription.cancel();
|
||||
ipNotifier.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,10 @@ class _StartButtonState extends State<StartButton>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
isStart = globalState.appController.appFlowingState.isStart;
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
value: 0,
|
||||
value: isStart ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
}
|
||||
@@ -85,58 +86,58 @@ class _StartButtonState extends State<StartButton>
|
||||
)
|
||||
.width +
|
||||
16;
|
||||
return AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
builder: (_, child) {
|
||||
return SizedBox(
|
||||
width: 56 + textWidth * _controller.value,
|
||||
height: 56,
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: () {
|
||||
handleSwitchStart();
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 56,
|
||||
height: 56,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedIcon(
|
||||
icon: AnimatedIcons.play_pause,
|
||||
progress: _controller,
|
||||
return _updateControllerContainer(
|
||||
AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
builder: (_, child) {
|
||||
return SizedBox(
|
||||
width: 56 + textWidth * _controller.value,
|
||||
height: 56,
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: () {
|
||||
handleSwitchStart();
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 56,
|
||||
height: 56,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedIcon(
|
||||
icon: AnimatedIcons.play_pause,
|
||||
progress: _controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ClipRect(
|
||||
child: OverflowBox(
|
||||
maxWidth: textWidth,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: child!,
|
||||
Expanded(
|
||||
child: ClipRect(
|
||||
child: OverflowBox(
|
||||
maxWidth: textWidth,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: child!,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: _updateControllerContainer(
|
||||
Selector<AppFlowingState, int?>(
|
||||
selector: (_, appFlowingState) => appFlowingState.runTime,
|
||||
builder: (_, int? value, __) {
|
||||
final text = other.getTimeText(value);
|
||||
return Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.titleMedium?.toSoftBold,
|
||||
);
|
||||
},
|
||||
),
|
||||
child: Selector<AppFlowingState, int?>(
|
||||
selector: (_, appFlowingState) => appFlowingState.runTime,
|
||||
builder: (_, int? value, __) {
|
||||
final text = other.getTimeText(value);
|
||||
return Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.titleMedium?.toSoftBold,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,45 +1,33 @@
|
||||
import 'package:fl_clash/common/app_localizations.dart';
|
||||
import 'package:fl_clash/common/system.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
// class VPNSwitch extends StatelessWidget {
|
||||
// const VPNSwitch({super.key});
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return SwitchContainer(
|
||||
// info: const Info(
|
||||
// label: "VPN",
|
||||
// iconData: Icons.stacked_line_chart,
|
||||
// ),
|
||||
// child: Selector<Config, bool>(
|
||||
// selector: (_, config) => config.vpnProps.enable,
|
||||
// builder: (_, enable, __) {
|
||||
// return Switch(
|
||||
// materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
// value: enable,
|
||||
// onChanged: (value) {
|
||||
// final config = globalState.appController.config;
|
||||
// config.vpnProps = config.vpnProps.copyWith(
|
||||
// enable: value,
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
import '../config/network.dart';
|
||||
|
||||
class TUNSwitch extends StatelessWidget {
|
||||
const TUNSwitch({super.key});
|
||||
class TUNButton extends StatelessWidget {
|
||||
const TUNButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchContainer(
|
||||
return ButtonContainer(
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return generateListView(generateSection(
|
||||
items: [
|
||||
if (system.isDesktop) const TUNItem(),
|
||||
const TunStackItem(),
|
||||
],
|
||||
));
|
||||
},
|
||||
title: appLocalizations.tun,
|
||||
);
|
||||
},
|
||||
info: Info(
|
||||
label: appLocalizations.tun,
|
||||
iconData: Icons.stacked_line_chart,
|
||||
@@ -64,18 +52,34 @@ class TUNSwitch extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class ProxySwitch extends StatelessWidget {
|
||||
const ProxySwitch({super.key});
|
||||
class SystemProxyButton extends StatelessWidget {
|
||||
const SystemProxyButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SwitchContainer(
|
||||
return ButtonContainer(
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return generateListView(
|
||||
generateSection(
|
||||
items: [
|
||||
SystemProxyItem(),
|
||||
BypassDomainItem(),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
title: appLocalizations.systemProxy,
|
||||
);
|
||||
},
|
||||
info: Info(
|
||||
label: appLocalizations.systemProxy,
|
||||
iconData: Icons.shuffle,
|
||||
),
|
||||
child: Selector<Config, bool>(
|
||||
selector: (_, config) => config.desktopProps.systemProxy,
|
||||
selector: (_, config) => config.networkProps.systemProxy,
|
||||
builder: (_, systemProxy, __) {
|
||||
return LocaleBuilder(
|
||||
builder: (_) => Switch(
|
||||
@@ -83,8 +87,8 @@ class ProxySwitch extends StatelessWidget {
|
||||
value: systemProxy,
|
||||
onChanged: (value) {
|
||||
final config = globalState.appController.config;
|
||||
config.desktopProps =
|
||||
config.desktopProps.copyWith(systemProxy: value);
|
||||
config.networkProps =
|
||||
config.networkProps.copyWith(systemProxy: value);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -94,20 +98,22 @@ class ProxySwitch extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class SwitchContainer extends StatelessWidget {
|
||||
class ButtonContainer extends StatelessWidget {
|
||||
final Info info;
|
||||
final Widget child;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
const SwitchContainer({
|
||||
const ButtonContainer({
|
||||
super.key,
|
||||
required this.info,
|
||||
required this.child,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonCard(
|
||||
onPressed: () {},
|
||||
onPressed: onPressed,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -7,7 +7,10 @@ import 'package:flutter/material.dart';
|
||||
class AddProfile extends StatelessWidget {
|
||||
final BuildContext context;
|
||||
|
||||
const AddProfile({super.key, required this.context,});
|
||||
const AddProfile({
|
||||
super.key,
|
||||
required this.context,
|
||||
});
|
||||
|
||||
_handleAddProfileFormFile() async {
|
||||
globalState.appController.addProfileFormFile();
|
||||
@@ -18,14 +21,16 @@ class AddProfile extends StatelessWidget {
|
||||
}
|
||||
|
||||
_toScan() async {
|
||||
if(system.isDesktop){
|
||||
if (system.isDesktop) {
|
||||
globalState.appController.addProfileFormQrCode();
|
||||
return;
|
||||
}
|
||||
final url = await Navigator.of(context)
|
||||
.push<String>(MaterialPageRoute(builder: (_) => const ScanPage()));
|
||||
final url = await BaseNavigator.push(
|
||||
context,
|
||||
const ScanPage(),
|
||||
);
|
||||
if (url != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_){
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_handleAddProfileFormURL(url);
|
||||
});
|
||||
}
|
||||
@@ -44,12 +49,12 @@ class AddProfile extends StatelessWidget {
|
||||
Widget build(context) {
|
||||
return ListView(
|
||||
children: [
|
||||
ListItem(
|
||||
leading: const Icon(Icons.qr_code),
|
||||
title: Text(appLocalizations.qrcode),
|
||||
subtitle: Text(appLocalizations.qrcodeDesc),
|
||||
onTap: _toScan,
|
||||
),
|
||||
ListItem(
|
||||
leading: const Icon(Icons.qr_code),
|
||||
title: Text(appLocalizations.qrcode),
|
||||
subtitle: Text(appLocalizations.qrcodeDesc),
|
||||
onTap: _toScan,
|
||||
),
|
||||
ListItem(
|
||||
leading: const Icon(Icons.upload_file),
|
||||
title: Text(appLocalizations.file),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/fragments/profiles/edit_profile.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -80,7 +80,7 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
||||
}
|
||||
}
|
||||
|
||||
_initScaffoldState() {
|
||||
_initScaffold() {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
if (!mounted) return;
|
||||
@@ -112,71 +112,67 @@ class _ProfilesFragmentState extends State<ProfilesFragment> {
|
||||
iconSize: 26,
|
||||
),
|
||||
];
|
||||
commonScaffoldState?.floatingActionButton = FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _handleShowAddExtendPage,
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FloatLayout(
|
||||
floatingWidget: FloatWrapper(
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _handleShowAddExtendPage,
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
),
|
||||
return ActiveBuilder(
|
||||
label: "profiles",
|
||||
builder: (isCurrent, child) {
|
||||
if (isCurrent) {
|
||||
_initScaffold();
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: Selector2<AppState, Config, ProfilesSelectorState>(
|
||||
selector: (_, appState, config) => ProfilesSelectorState(
|
||||
profiles: config.profiles,
|
||||
currentProfileId: config.currentProfileId,
|
||||
columns: other.getProfilesColumns(appState.viewWidth),
|
||||
),
|
||||
),
|
||||
child: Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.currentLabel == 'profiles',
|
||||
builder: (_, isCurrent, child) {
|
||||
if (isCurrent) {
|
||||
_initScaffoldState();
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: Selector2<AppState, Config, ProfilesSelectorState>(
|
||||
selector: (_, appState, config) => ProfilesSelectorState(
|
||||
profiles: config.profiles,
|
||||
currentProfileId: config.currentProfileId,
|
||||
columns: other.getProfilesColumns(appState.viewWidth),
|
||||
),
|
||||
builder: (context, state, child) {
|
||||
if (state.profiles.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullProfileDesc,
|
||||
);
|
||||
}
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
bottom: 88,
|
||||
),
|
||||
child: Grid(
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisSpacing: 16,
|
||||
crossAxisCount: state.columns,
|
||||
children: [
|
||||
for (int i = 0; i < state.profiles.length; i++)
|
||||
GridItem(
|
||||
child: ProfileItem(
|
||||
key: Key(state.profiles[i].id),
|
||||
profile: state.profiles[i],
|
||||
groupValue: state.currentProfileId,
|
||||
onChanged: globalState.appController.changeProfile,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
builder: (context, state, child) {
|
||||
if (state.profiles.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullProfileDesc,
|
||||
);
|
||||
},
|
||||
),
|
||||
}
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
bottom: 88,
|
||||
),
|
||||
child: Grid(
|
||||
mainAxisSpacing: 16,
|
||||
crossAxisSpacing: 16,
|
||||
crossAxisCount: state.columns,
|
||||
children: [
|
||||
for (int i = 0; i < state.profiles.length; i++)
|
||||
GridItem(
|
||||
child: ProfileItem(
|
||||
key: Key(state.profiles[i].id),
|
||||
profile: state.profiles[i],
|
||||
groupValue: state.currentProfileId,
|
||||
onChanged: globalState.appController.changeProfile,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -250,44 +246,16 @@ class ProfileItem extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildUserInfo(BuildContext context, UserInfo userInfo) {
|
||||
final use = userInfo.upload + userInfo.download;
|
||||
final total = userInfo.total;
|
||||
if (total == 0) {
|
||||
return [];
|
||||
}
|
||||
final useShow = TrafficValue(value: use).show;
|
||||
final totalShow = TrafficValue(value: total).show;
|
||||
final progress = total == 0 ? 0.0 : use / total;
|
||||
final expireShow = userInfo.expire == 0
|
||||
? appLocalizations.infiniteTime
|
||||
: DateTime.fromMillisecondsSinceEpoch(userInfo.expire * 1000).show;
|
||||
return [
|
||||
LinearProgressIndicator(
|
||||
minHeight: 6,
|
||||
value: progress,
|
||||
backgroundColor: context.colorScheme.primary.toSoft(),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"$useShow / $totalShow · $expireShow",
|
||||
style: context.textTheme.labelMedium?.toLight,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildUrlProfileInfo(BuildContext context) {
|
||||
final userInfo = profile.userInfo;
|
||||
final subscriptionInfo = profile.subscriptionInfo;
|
||||
return [
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (userInfo != null) ..._buildUserInfo(context, userInfo),
|
||||
if (subscriptionInfo != null)
|
||||
SubscriptionInfoView(
|
||||
subscriptionInfo: subscriptionInfo,
|
||||
),
|
||||
Text(
|
||||
profile.lastUpdateDate?.lastUpdateTimeDesc ?? "",
|
||||
style: context.textTheme.labelMedium?.toLight,
|
||||
|
||||
@@ -51,7 +51,7 @@ class _ProxiesListFragmentState extends State<ProxiesListFragment> {
|
||||
}
|
||||
_headerStateNotifier.value = _headerStateNotifier.value.copyWith(
|
||||
currentIndex: currentIndex,
|
||||
offset: headerOffset,
|
||||
offset: max(headerOffset, 0),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -299,6 +299,9 @@ class _ProxiesListFragmentState extends State<ProxiesListFragment> {
|
||||
headerState.currentIndex > state.groupNames.length - 1
|
||||
? 0
|
||||
: headerState.currentIndex;
|
||||
if (index < 0) {
|
||||
return Container();
|
||||
}
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
@@ -417,9 +420,9 @@ class _ListHeaderState extends State<ListHeader>
|
||||
final iconMapEntryList =
|
||||
config.proxiesStyle.iconMap.entries.toList();
|
||||
final index = iconMapEntryList.indexWhere((item) {
|
||||
try{
|
||||
try {
|
||||
return RegExp(item.key).hasMatch(groupName);
|
||||
}catch(_){
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@@ -468,7 +471,7 @@ class _ListHeaderState extends State<ListHeader>
|
||||
Widget build(BuildContext context) {
|
||||
return CommonCard(
|
||||
key: widget.key,
|
||||
radius: 24,
|
||||
radius: 18,
|
||||
type: CommonCardType.filled,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
||||
@@ -27,6 +27,7 @@ class _ProvidersState extends State<Providers> {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
globalState.appController.updateProviders();
|
||||
final commonScaffoldState =
|
||||
context.findAncestorStateOfType<CommonScaffoldState>();
|
||||
commonScaffoldState?.actions = [
|
||||
@@ -132,8 +133,8 @@ class ProviderItem extends StatelessWidget {
|
||||
final platformFile = await picker.pickerFile();
|
||||
final appState = globalState.appController.appState;
|
||||
final bytes = platformFile?.bytes;
|
||||
if (bytes == null) return;
|
||||
final file = await File(provider.path).create(recursive: true);
|
||||
if (bytes == null || provider.path == null) return;
|
||||
final file = await File(provider.path!).create(recursive: true);
|
||||
await file.writeAsBytes(bytes);
|
||||
final providerName = provider.name;
|
||||
var message = await clashCore.sideLoadExternalProvider(
|
||||
@@ -150,8 +151,7 @@ class ProviderItem extends StatelessWidget {
|
||||
}
|
||||
|
||||
String _buildProviderDesc() {
|
||||
final baseInfo =
|
||||
"${provider.type}(${provider.vehicleType}) · ${provider.updateAt.lastUpdateTimeDesc}";
|
||||
final baseInfo = provider.updateAt.lastUpdateTimeDesc;
|
||||
final count = provider.count;
|
||||
return switch (count == 0) {
|
||||
true => baseInfo,
|
||||
@@ -176,10 +176,13 @@ class ProviderItem extends StatelessWidget {
|
||||
Text(
|
||||
_buildProviderDesc(),
|
||||
),
|
||||
Text(
|
||||
provider.path,
|
||||
style: context.textTheme.bodyMedium?.toLight,
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
if (provider.subscriptionInfo != null)
|
||||
SubscriptionInfoView(
|
||||
subscriptionInfo: provider.subscriptionInfo,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
@@ -200,6 +203,9 @@ class ProviderItem extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: SizedBox(
|
||||
|
||||
@@ -191,7 +191,7 @@ class ProxiesSetting extends StatelessWidget {
|
||||
|
||||
_buildGroupStyleSetting() {
|
||||
return generateSection(
|
||||
title: "图标样式",
|
||||
title: appLocalizations.iconStyle,
|
||||
items: [
|
||||
SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
|
||||
@@ -278,6 +278,23 @@ class ProxyGroupViewState extends State<ProxyGroupView> {
|
||||
);
|
||||
}
|
||||
|
||||
initFab(bool isCurrent, List<Proxy> proxies) {
|
||||
if (!isCurrent) {
|
||||
return;
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final commonScaffoldState =
|
||||
context.findAncestorStateOfType<CommonScaffoldState>();
|
||||
commonScaffoldState?.floatingActionButton = DelayTestButton(
|
||||
onClick: () async {
|
||||
await _delayTest(
|
||||
proxies,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector2<AppState, Config, ProxyGroupSelectorState>(
|
||||
@@ -303,11 +320,11 @@ class ProxyGroupViewState extends State<ProxyGroupView> {
|
||||
proxies,
|
||||
);
|
||||
_lastProxies = sortedProxies;
|
||||
return DelayTestButtonContainer(
|
||||
onClick: () async {
|
||||
await _delayTest(
|
||||
proxies,
|
||||
);
|
||||
return ActiveBuilder(
|
||||
label: "proxies",
|
||||
builder: (isCurrent, child) {
|
||||
initFab(isCurrent, proxies);
|
||||
return child!;
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
@@ -344,22 +361,19 @@ class ProxyGroupViewState extends State<ProxyGroupView> {
|
||||
}
|
||||
}
|
||||
|
||||
class DelayTestButtonContainer extends StatefulWidget {
|
||||
final Widget child;
|
||||
class DelayTestButton extends StatefulWidget {
|
||||
final Future Function() onClick;
|
||||
|
||||
const DelayTestButtonContainer({
|
||||
const DelayTestButton({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.onClick,
|
||||
});
|
||||
|
||||
@override
|
||||
State<DelayTestButtonContainer> createState() =>
|
||||
_DelayTestButtonContainerState();
|
||||
State<DelayTestButton> createState() => _DelayTestButtonState();
|
||||
}
|
||||
|
||||
class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
|
||||
class _DelayTestButtonState extends State<DelayTestButton>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _scale;
|
||||
@@ -401,29 +415,23 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_controller.reverse();
|
||||
return FloatLayout(
|
||||
floatingWidget: FloatWrapper(
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
builder: (_, child) {
|
||||
return SizedBox(
|
||||
width: 56,
|
||||
height: 56,
|
||||
child: Transform.scale(
|
||||
scale: _scale.value,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _healthcheck,
|
||||
child: const Icon(Icons.network_ping),
|
||||
return AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
builder: (_, child) {
|
||||
return SizedBox(
|
||||
width: 56,
|
||||
height: 56,
|
||||
child: Transform.scale(
|
||||
scale: _scale.value,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: FloatingActionButton(
|
||||
heroTag: null,
|
||||
onPressed: _healthcheck,
|
||||
child: const Icon(Icons.network_ping),
|
||||
),
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ class _ThemeColorsBoxState extends State<ThemeColorsBox> {
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
||||
@@ -168,6 +168,7 @@
|
||||
"ipv6Desc": "When turned on it will be able to receive IPv6 traffic",
|
||||
"app": "App",
|
||||
"general": "General",
|
||||
"vpnSystemProxyDesc": "Attach HTTP proxy to VpnService",
|
||||
"systemProxyDesc": "Attach HTTP proxy to VpnService",
|
||||
"unifiedDelay": "Unified delay",
|
||||
"unifiedDelayDesc": "Remove extra delays such as handshaking",
|
||||
@@ -322,5 +323,12 @@
|
||||
"adminAutoLaunch": "Admin auto launch",
|
||||
"adminAutoLaunchDesc": "Boot up by using admin mode",
|
||||
"fontFamily": "FontFamily",
|
||||
"systemFont": "System font"
|
||||
"systemFont": "System font",
|
||||
"toggle": "Toggle",
|
||||
"system": "System",
|
||||
"routeMode": "Route mode",
|
||||
"routeMode_bypassPrivate": "Bypass private route address",
|
||||
"routeMode_config": "Use config",
|
||||
"routeAddress": "Route address",
|
||||
"routeAddressDesc": "Config listen route address"
|
||||
}
|
||||
@@ -168,7 +168,8 @@
|
||||
"ipv6Desc": "开启后将可以接收IPv6流量",
|
||||
"app": "应用",
|
||||
"general": "基础",
|
||||
"systemProxyDesc": "为VpnService附加HTTP代理",
|
||||
"vpnSystemProxyDesc": "为VpnService附加HTTP代理",
|
||||
"systemProxyDesc": "设置系统代理",
|
||||
"unifiedDelay": "统一延迟",
|
||||
"unifiedDelayDesc": "去除握手等额外延迟",
|
||||
"tcpConcurrent": "TCP并发",
|
||||
@@ -322,5 +323,12 @@
|
||||
"adminAutoLaunch": "管理员自启动",
|
||||
"adminAutoLaunchDesc": "使用管理员模式开机自启动",
|
||||
"fontFamily": "字体",
|
||||
"systemFont": "系统字体"
|
||||
}
|
||||
"systemFont": "系统字体",
|
||||
"toggle": "切换",
|
||||
"system": "系统",
|
||||
"routeMode": "路由模式",
|
||||
"routeMode_bypassPrivate": "绕过私有路由地址",
|
||||
"routeMode_config": "使用配置",
|
||||
"routeAddress": "路由地址",
|
||||
"routeAddressDesc": "配置监听路由地址"
|
||||
}
|
||||
|
||||
@@ -398,6 +398,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRules": MessageLookupByLibrary.simpleMessage("Respect rules"),
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS connection following rules, need to configure proxy-server-nameserver"),
|
||||
"routeAddress": MessageLookupByLibrary.simpleMessage("Route address"),
|
||||
"routeAddressDesc":
|
||||
MessageLookupByLibrary.simpleMessage("Config listen route address"),
|
||||
"routeMode": MessageLookupByLibrary.simpleMessage("Route mode"),
|
||||
"routeMode_bypassPrivate": MessageLookupByLibrary.simpleMessage(
|
||||
"Bypass private route address"),
|
||||
"routeMode_config": MessageLookupByLibrary.simpleMessage("Use config"),
|
||||
"rule": MessageLookupByLibrary.simpleMessage("Rule"),
|
||||
"ruleProviders": MessageLookupByLibrary.simpleMessage("Rule providers"),
|
||||
"save": MessageLookupByLibrary.simpleMessage("Save"),
|
||||
@@ -426,6 +433,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"style": MessageLookupByLibrary.simpleMessage("Style"),
|
||||
"submit": MessageLookupByLibrary.simpleMessage("Submit"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("Sync"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("System"),
|
||||
"systemFont": MessageLookupByLibrary.simpleMessage("System font"),
|
||||
"systemProxy": MessageLookupByLibrary.simpleMessage("System proxy"),
|
||||
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -447,6 +455,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tight": MessageLookupByLibrary.simpleMessage("Tight"),
|
||||
"time": MessageLookupByLibrary.simpleMessage("Time"),
|
||||
"tip": MessageLookupByLibrary.simpleMessage("tip"),
|
||||
"toggle": MessageLookupByLibrary.simpleMessage("Toggle"),
|
||||
"tools": MessageLookupByLibrary.simpleMessage("Tools"),
|
||||
"trafficUsage": MessageLookupByLibrary.simpleMessage("Traffic usage"),
|
||||
"tun": MessageLookupByLibrary.simpleMessage("TUN"),
|
||||
@@ -474,6 +483,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Modify VPN related settings"),
|
||||
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Auto routes all system traffic through VpnService"),
|
||||
"vpnSystemProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Attach HTTP proxy to VpnService"),
|
||||
"vpnTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Changes take effect after restarting the VPN"),
|
||||
"webDAVConfiguration":
|
||||
|
||||
@@ -314,6 +314,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRules": MessageLookupByLibrary.simpleMessage("遵守规则"),
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS连接跟随rules,需配置proxy-server-nameserver"),
|
||||
"routeAddress": MessageLookupByLibrary.simpleMessage("路由地址"),
|
||||
"routeAddressDesc": MessageLookupByLibrary.simpleMessage("配置监听路由地址"),
|
||||
"routeMode": MessageLookupByLibrary.simpleMessage("路由模式"),
|
||||
"routeMode_bypassPrivate":
|
||||
MessageLookupByLibrary.simpleMessage("绕过私有路由地址"),
|
||||
"routeMode_config": MessageLookupByLibrary.simpleMessage("使用配置"),
|
||||
"rule": MessageLookupByLibrary.simpleMessage("规则"),
|
||||
"ruleProviders": MessageLookupByLibrary.simpleMessage("规则提供者"),
|
||||
"save": MessageLookupByLibrary.simpleMessage("保存"),
|
||||
@@ -340,10 +346,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"style": MessageLookupByLibrary.simpleMessage("风格"),
|
||||
"submit": MessageLookupByLibrary.simpleMessage("提交"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("同步"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("系统"),
|
||||
"systemFont": MessageLookupByLibrary.simpleMessage("系统字体"),
|
||||
"systemProxy": MessageLookupByLibrary.simpleMessage("系统代理"),
|
||||
"systemProxyDesc":
|
||||
MessageLookupByLibrary.simpleMessage("为VpnService附加HTTP代理"),
|
||||
"systemProxyDesc": MessageLookupByLibrary.simpleMessage("设置系统代理"),
|
||||
"tab": MessageLookupByLibrary.simpleMessage("标签页"),
|
||||
"tabAnimation": MessageLookupByLibrary.simpleMessage("选项卡动画"),
|
||||
"tabAnimationDesc":
|
||||
@@ -359,6 +365,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tight": MessageLookupByLibrary.simpleMessage("紧凑"),
|
||||
"time": MessageLookupByLibrary.simpleMessage("时间"),
|
||||
"tip": MessageLookupByLibrary.simpleMessage("提示"),
|
||||
"toggle": MessageLookupByLibrary.simpleMessage("切换"),
|
||||
"tools": MessageLookupByLibrary.simpleMessage("工具"),
|
||||
"trafficUsage": MessageLookupByLibrary.simpleMessage("流量统计"),
|
||||
"tun": MessageLookupByLibrary.simpleMessage("虚拟网卡"),
|
||||
@@ -380,6 +387,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"),
|
||||
"vpnEnableDesc":
|
||||
MessageLookupByLibrary.simpleMessage("通过VpnService自动路由系统所有流量"),
|
||||
"vpnSystemProxyDesc":
|
||||
MessageLookupByLibrary.simpleMessage("为VpnService附加HTTP代理"),
|
||||
"vpnTip": MessageLookupByLibrary.simpleMessage("重启VPN后改变生效"),
|
||||
"webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV配置"),
|
||||
"whitelistMode": MessageLookupByLibrary.simpleMessage("白名单模式"),
|
||||
|
||||
@@ -1740,6 +1740,16 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Attach HTTP proxy to VpnService`
|
||||
String get vpnSystemProxyDesc {
|
||||
return Intl.message(
|
||||
'Attach HTTP proxy to VpnService',
|
||||
name: 'vpnSystemProxyDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Attach HTTP proxy to VpnService`
|
||||
String get systemProxyDesc {
|
||||
return Intl.message(
|
||||
@@ -3289,6 +3299,76 @@ class AppLocalizations {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Toggle`
|
||||
String get toggle {
|
||||
return Intl.message(
|
||||
'Toggle',
|
||||
name: 'toggle',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `System`
|
||||
String get system {
|
||||
return Intl.message(
|
||||
'System',
|
||||
name: 'system',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Route mode`
|
||||
String get routeMode {
|
||||
return Intl.message(
|
||||
'Route mode',
|
||||
name: 'routeMode',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Bypass private route address`
|
||||
String get routeMode_bypassPrivate {
|
||||
return Intl.message(
|
||||
'Bypass private route address',
|
||||
name: 'routeMode_bypassPrivate',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Use config`
|
||||
String get routeMode_config {
|
||||
return Intl.message(
|
||||
'Use config',
|
||||
name: 'routeMode_config',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Route address`
|
||||
String get routeAddress {
|
||||
return Intl.message(
|
||||
'Route address',
|
||||
name: 'routeAddress',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Config listen route address`
|
||||
String get routeAddressDesc {
|
||||
return Intl.message(
|
||||
'Config listen route address',
|
||||
name: 'routeAddressDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -19,6 +19,10 @@ Future<void> main() async {
|
||||
globalState.packageInfo = await PackageInfo.fromPlatform();
|
||||
final version = await system.version;
|
||||
final config = await preferences.getConfig() ?? Config();
|
||||
await AppLocalizations.load(
|
||||
other.getLocaleForString(config.appSetting.locale) ??
|
||||
WidgetsBinding.instance.platformDispatcher.locale,
|
||||
);
|
||||
final clashConfig = await preferences.getClashConfig() ?? ClashConfig();
|
||||
await android?.init();
|
||||
await window?.init(config.windowProps, version);
|
||||
@@ -27,10 +31,17 @@ Future<void> main() async {
|
||||
version: version,
|
||||
selectedMap: config.currentSelectedMap,
|
||||
);
|
||||
final appFlowingState = AppFlowingState();
|
||||
appState.navigationItems = navigation.getItems(
|
||||
openLogs: config.appSetting.openLogs,
|
||||
hasProxies: false,
|
||||
);
|
||||
globalState.updateTray(
|
||||
appState: appState,
|
||||
appFlowingState: appFlowingState,
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
await globalState.init(
|
||||
appState: appState,
|
||||
config: config,
|
||||
@@ -40,6 +51,7 @@ Future<void> main() async {
|
||||
runAppWithPreferences(
|
||||
const Application(),
|
||||
appState: appState,
|
||||
appFlowingState: appFlowingState,
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
@@ -53,6 +65,10 @@ Future<void> vpnService() async {
|
||||
final version = await system.version;
|
||||
final config = await preferences.getConfig() ?? Config();
|
||||
final clashConfig = await preferences.getClashConfig() ?? ClashConfig();
|
||||
await AppLocalizations.load(
|
||||
other.getLocaleForString(config.appSetting.locale) ??
|
||||
WidgetsBinding.instance.platformDispatcher.locale,
|
||||
);
|
||||
final appState = AppState(
|
||||
mode: clashConfig.mode,
|
||||
selectedMap: config.currentSelectedMap,
|
||||
@@ -98,15 +114,8 @@ Future<void> vpnService() async {
|
||||
},
|
||||
),
|
||||
);
|
||||
final appLocalizations = await AppLocalizations.load(
|
||||
other.getLocaleForString(config.appSetting.locale) ??
|
||||
WidgetsBinding.instance.platformDispatcher.locale,
|
||||
);
|
||||
await app?.tip(appLocalizations.startVpn);
|
||||
await globalState.handleStart(
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
await globalState.handleStart();
|
||||
|
||||
tile?.addListener(
|
||||
TileListenerWithVpn(
|
||||
|
||||
@@ -18,7 +18,6 @@ class AppStateManager extends StatefulWidget {
|
||||
|
||||
class _AppStateManagerState extends State<AppStateManager>
|
||||
with WidgetsBindingObserver {
|
||||
|
||||
_updateNavigationsContainer(Widget child) {
|
||||
return Selector2<AppState, Config, UpdateNavigationsSelector>(
|
||||
selector: (_, appState, config) {
|
||||
@@ -45,6 +44,22 @@ class _AppStateManagerState extends State<AppStateManager>
|
||||
);
|
||||
}
|
||||
|
||||
_cacheStateChange(Widget child) {
|
||||
return Selector2<Config, ClashConfig, String>(
|
||||
selector: (_, config, clashConfig) => "$clashConfig $config",
|
||||
shouldRebuild: (prev, next) {
|
||||
if (prev != next) {
|
||||
globalState.appController.savePreferencesDebounce();
|
||||
}
|
||||
return prev != next;
|
||||
},
|
||||
builder: (context, state, child) {
|
||||
return child!;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -61,7 +76,7 @@ class _AppStateManagerState extends State<AppStateManager>
|
||||
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||
final isPaused = state == AppLifecycleState.paused;
|
||||
if (isPaused) {
|
||||
await globalState.appController.savePreferences();
|
||||
globalState.appController.savePreferencesDebounce();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +88,10 @@ class _AppStateManagerState extends State<AppStateManager>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _updateNavigationsContainer(
|
||||
widget.child,
|
||||
return _cacheStateChange(
|
||||
_updateNavigationsContainer(
|
||||
widget.child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ class ClashManager extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ClashContainerState extends State<ClashManager> with AppMessageListener {
|
||||
Function? updateClashConfigDebounce;
|
||||
Function? updateDelayDebounce;
|
||||
|
||||
Widget _updateContainer(Widget child) {
|
||||
@@ -47,10 +46,7 @@ class _ClashContainerState extends State<ClashManager> with AppMessageListener {
|
||||
),
|
||||
shouldRebuild: (prev, next) {
|
||||
if (prev != next) {
|
||||
updateClashConfigDebounce ??= debounce<Function()>(() async {
|
||||
await globalState.appController.updateClashConfig();
|
||||
});
|
||||
updateClashConfigDebounce!();
|
||||
globalState.appController.updateClashConfigDebounce();
|
||||
}
|
||||
return prev != next;
|
||||
},
|
||||
@@ -68,11 +64,12 @@ class _ClashContainerState extends State<ClashManager> with AppMessageListener {
|
||||
accessControl: config.isAccessControl ? config.accessControl : null,
|
||||
ipv6: config.vpnProps.ipv6,
|
||||
allowBypass: config.vpnProps.allowBypass,
|
||||
bypassDomain: config.vpnProps.bypassDomain,
|
||||
bypassDomain: config.networkProps.bypassDomain,
|
||||
systemProxy: config.vpnProps.systemProxy,
|
||||
onlyProxy: config.appSetting.onlyProxy,
|
||||
currentProfileName:
|
||||
config.currentProfile?.label ?? config.currentProfileId ?? "",
|
||||
routeAddress: clashConfig.routeAddress,
|
||||
),
|
||||
builder: (__, state, child) {
|
||||
clashCore.setState(state);
|
||||
|
||||
@@ -8,13 +8,13 @@ class ProxyManager extends StatelessWidget {
|
||||
|
||||
const ProxyManager({super.key, required this.child});
|
||||
|
||||
_updateProxy(ProxyState proxyState) {
|
||||
_updateProxy(ProxyState proxyState) async {
|
||||
final isStart = proxyState.isStart;
|
||||
final systemProxy = proxyState.systemProxy;
|
||||
final port = proxyState.port;
|
||||
if (isStart && systemProxy) {
|
||||
proxy?.startProxy(port);
|
||||
}else{
|
||||
proxy?.startProxy(port, proxyState.bassDomain);
|
||||
} else {
|
||||
proxy?.stopProxy();
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,9 @@ class ProxyManager extends StatelessWidget {
|
||||
return Selector3<AppFlowingState, Config, ClashConfig, ProxyState>(
|
||||
selector: (_, appFlowingState, config, clashConfig) => ProxyState(
|
||||
isStart: appFlowingState.isStart,
|
||||
systemProxy: config.desktopProps.systemProxy,
|
||||
systemProxy: config.networkProps.systemProxy,
|
||||
port: clashConfig.mixedPort,
|
||||
bassDomain: config.networkProps.bypassDomain,
|
||||
),
|
||||
builder: (_, state, child) {
|
||||
_updateProxy(state);
|
||||
|
||||
@@ -15,8 +15,6 @@ class TileManager extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _TileContainerState extends State<TileManager> with TileListener {
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
@@ -29,7 +27,7 @@ class _TileContainerState extends State<TileManager> with TileListener {
|
||||
}
|
||||
|
||||
@override
|
||||
void onStop() {
|
||||
Future<void> onStop() async {
|
||||
globalState.appController.updateStatus(false);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
import 'package:window_ext/window_ext.dart';
|
||||
|
||||
class TrayManager extends StatefulWidget {
|
||||
final Widget child;
|
||||
@@ -22,8 +17,7 @@ class TrayManager extends StatefulWidget {
|
||||
State<TrayManager> createState() => _TrayContainerState();
|
||||
}
|
||||
|
||||
class _TrayContainerState extends State<TrayManager>
|
||||
with TrayListener {
|
||||
class _TrayContainerState extends State<TrayManager> with TrayListener {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -40,15 +34,17 @@ class _TrayContainerState extends State<TrayManager>
|
||||
autoLaunch: config.appSetting.autoLaunch,
|
||||
isStart: appFlowingState.isStart,
|
||||
locale: config.appSetting.locale,
|
||||
systemProxy: config.desktopProps.systemProxy,
|
||||
systemProxy: config.networkProps.systemProxy,
|
||||
tunEnable: clashConfig.tun.enable,
|
||||
brightness: appState.brightness,
|
||||
),
|
||||
shouldRebuild: (prev, next) {
|
||||
if (prev != next) {
|
||||
globalState.appController.updateTray();
|
||||
}
|
||||
return prev != next;
|
||||
},
|
||||
builder: (_, state, child) {
|
||||
globalState.appController.updateTray();
|
||||
return child!;
|
||||
},
|
||||
child: widget.child,
|
||||
|
||||
@@ -20,7 +20,8 @@ class WindowManager extends StatefulWidget {
|
||||
State<WindowManager> createState() => _WindowContainerState();
|
||||
}
|
||||
|
||||
class _WindowContainerState extends State<WindowManager> with WindowListener, WindowExtListener {
|
||||
class _WindowContainerState extends State<WindowManager>
|
||||
with WindowListener, WindowExtListener {
|
||||
Function? updateLaunchDebounce;
|
||||
|
||||
_autoLaunchContainer(Widget child) {
|
||||
@@ -58,15 +59,15 @@ class _WindowContainerState extends State<WindowManager> with WindowListener, Wi
|
||||
super.onWindowClose();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onShouldTerminate() async {
|
||||
await globalState.appController.handleExit();
|
||||
super.onShouldTerminate();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onWindowMoved() async {
|
||||
super.onWindowMoved();
|
||||
final offset = await windowManager.getPosition();
|
||||
final config = globalState.appController.config;
|
||||
config.windowProps = config.windowProps.copyWith(
|
||||
top: offset.dy,
|
||||
left: offset.dx,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -82,7 +83,7 @@ class _WindowContainerState extends State<WindowManager> with WindowListener, Wi
|
||||
|
||||
@override
|
||||
void onWindowMinimize() async {
|
||||
await globalState.appController.savePreferences();
|
||||
globalState.appController.savePreferencesDebounce();
|
||||
super.onWindowMinimize();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../enum/enum.dart';
|
||||
|
||||
part 'generated/clash_config.g.dart';
|
||||
|
||||
part 'generated/clash_config.freezed.dart';
|
||||
part 'generated/clash_config.g.dart';
|
||||
|
||||
const defaultTun = Tun();
|
||||
|
||||
@@ -126,6 +125,91 @@ typedef HostsMap = Map<String, String>;
|
||||
const defaultMixedPort = 7890;
|
||||
const defaultKeepAliveInterval = 30;
|
||||
|
||||
const defaultBypassPrivateRouteAddress = [
|
||||
"1.0.0.0/8",
|
||||
"2.0.0.0/7",
|
||||
"4.0.0.0/6",
|
||||
"8.0.0.0/7",
|
||||
"11.0.0.0/8",
|
||||
"12.0.0.0/6",
|
||||
"16.0.0.0/4",
|
||||
"32.0.0.0/3",
|
||||
"64.0.0.0/3",
|
||||
"96.0.0.0/4",
|
||||
"112.0.0.0/5",
|
||||
"120.0.0.0/6",
|
||||
"124.0.0.0/7",
|
||||
"126.0.0.0/8",
|
||||
"128.0.0.0/3",
|
||||
"160.0.0.0/5",
|
||||
"168.0.0.0/8",
|
||||
"169.0.0.0/9",
|
||||
"169.128.0.0/10",
|
||||
"169.192.0.0/11",
|
||||
"169.224.0.0/12",
|
||||
"169.240.0.0/13",
|
||||
"169.248.0.0/14",
|
||||
"169.252.0.0/15",
|
||||
"169.255.0.0/16",
|
||||
"170.0.0.0/7",
|
||||
"172.0.0.0/12",
|
||||
"172.32.0.0/11",
|
||||
"172.64.0.0/10",
|
||||
"172.128.0.0/9",
|
||||
"173.0.0.0/8",
|
||||
"174.0.0.0/7",
|
||||
"176.0.0.0/4",
|
||||
"192.0.0.0/9",
|
||||
"192.128.0.0/11",
|
||||
"192.160.0.0/13",
|
||||
"192.169.0.0/16",
|
||||
"192.170.0.0/15",
|
||||
"192.172.0.0/14",
|
||||
"192.176.0.0/12",
|
||||
"192.192.0.0/10",
|
||||
"193.0.0.0/8",
|
||||
"194.0.0.0/7",
|
||||
"196.0.0.0/6",
|
||||
"200.0.0.0/5",
|
||||
"208.0.0.0/4",
|
||||
"240.0.0.0/5",
|
||||
"248.0.0.0/6",
|
||||
"252.0.0.0/7",
|
||||
"254.0.0.0/8",
|
||||
"255.0.0.0/9",
|
||||
"255.128.0.0/10",
|
||||
"255.192.0.0/11",
|
||||
"255.224.0.0/12",
|
||||
"255.240.0.0/13",
|
||||
"255.248.0.0/14",
|
||||
"255.252.0.0/15",
|
||||
"255.254.0.0/16",
|
||||
"255.255.0.0/17",
|
||||
"255.255.128.0/18",
|
||||
"255.255.192.0/19",
|
||||
"255.255.224.0/20",
|
||||
"255.255.240.0/21",
|
||||
"255.255.248.0/22",
|
||||
"255.255.252.0/23",
|
||||
"255.255.254.0/24",
|
||||
"255.255.255.0/25",
|
||||
"255.255.255.128/26",
|
||||
"255.255.255.192/27",
|
||||
"255.255.255.224/28",
|
||||
"255.255.255.240/29",
|
||||
"255.255.255.248/30",
|
||||
"255.255.255.252/31",
|
||||
"255.255.255.254/32",
|
||||
"::/1",
|
||||
"8000::/2",
|
||||
"c000::/3",
|
||||
"e000::/4",
|
||||
"f000::/5",
|
||||
"f800::/6",
|
||||
"fe00::/9",
|
||||
"fec0::/10"
|
||||
];
|
||||
|
||||
@JsonSerializable()
|
||||
class ClashConfig extends ChangeNotifier {
|
||||
int _mixedPort;
|
||||
@@ -145,6 +229,8 @@ class ClashConfig extends ChangeNotifier {
|
||||
List<String> _rules;
|
||||
String? _globalRealUa;
|
||||
HostsMap _hosts;
|
||||
List<String> _includeRouteAddress;
|
||||
RouteMode _routeMode;
|
||||
|
||||
ClashConfig()
|
||||
: _mixedPort = defaultMixedPort,
|
||||
@@ -161,6 +247,8 @@ class ClashConfig extends ChangeNotifier {
|
||||
_keepAliveInterval = defaultKeepAliveInterval,
|
||||
_dns = defaultDns,
|
||||
_geoXUrl = defaultGeoXMap,
|
||||
_routeMode = RouteMode.config,
|
||||
_includeRouteAddress = [],
|
||||
_rules = [],
|
||||
_hosts = {};
|
||||
|
||||
@@ -343,6 +431,34 @@ class ClashConfig extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonKey(name: "route-address", includeFromJson: false, includeToJson: true)
|
||||
List<String> get routeAddress {
|
||||
return switch (_routeMode == RouteMode.config) {
|
||||
true => _includeRouteAddress,
|
||||
false => defaultBypassPrivateRouteAddress,
|
||||
};
|
||||
}
|
||||
|
||||
@JsonKey(name: "include-route-address", defaultValue: [])
|
||||
List<String> get includeRouteAddress => _includeRouteAddress;
|
||||
|
||||
set includeRouteAddress(List<String> value) {
|
||||
if (!stringListEquality.equals(value, _includeRouteAddress)) {
|
||||
_includeRouteAddress = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@JsonKey(name: "route-mode", defaultValue: RouteMode.config)
|
||||
RouteMode get routeMode => _routeMode;
|
||||
|
||||
set routeMode(RouteMode value) {
|
||||
if (value != _routeMode) {
|
||||
_routeMode = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
update([ClashConfig? clashConfig]) {
|
||||
if (clashConfig != null) {
|
||||
_mixedPort = clashConfig._mixedPort;
|
||||
@@ -360,6 +476,8 @@ class ClashConfig extends ChangeNotifier {
|
||||
_geodataLoader = clashConfig._geodataLoader;
|
||||
_dns = clashConfig._dns;
|
||||
_rules = clashConfig._rules;
|
||||
_routeMode = clashConfig._routeMode;
|
||||
_includeRouteAddress = clashConfig._includeRouteAddress;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -371,4 +489,9 @@ class ClashConfig extends ChangeNotifier {
|
||||
factory ClashConfig.fromJson(Map<String, dynamic> json) {
|
||||
return _$ClashConfigFromJson(json);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ClashConfig{_mixedPort: $_mixedPort, _allowLan: $_allowLan, _ipv6: $_ipv6, _geodataLoader: $_geodataLoader, _logLevel: $_logLevel, _externalController: $_externalController, _mode: $_mode, _findProcessMode: $_findProcessMode, _keepAliveInterval: $_keepAliveInterval, _unifiedDelay: $_unifiedDelay, _tcpConcurrent: $_tcpConcurrent, _tun: $_tun, _dns: $_dns, _geoXUrl: $_geoXUrl, _rules: $_rules, _globalRealUa: $_globalRealUa, _hosts: $_hosts}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +431,6 @@ class HotKeyAction with _$HotKeyAction {
|
||||
_$HotKeyActionFromJson(json);
|
||||
}
|
||||
|
||||
|
||||
typedef Validator = String? Function(String? value);
|
||||
|
||||
@freezed
|
||||
@@ -441,4 +440,4 @@ class Field with _$Field {
|
||||
required String value,
|
||||
Validator? validator,
|
||||
}) = _Field;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -6,11 +7,12 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'models.dart';
|
||||
|
||||
part 'generated/config.freezed.dart';
|
||||
part 'generated/config.g.dart';
|
||||
|
||||
part 'generated/config.freezed.dart';
|
||||
|
||||
const defaultAppSetting = AppSetting();
|
||||
final defaultAppSetting = const AppSetting().copyWith(
|
||||
isAnimateToPage: system.isDesktop ? false : true,
|
||||
);
|
||||
|
||||
@freezed
|
||||
class AppSetting with _$AppSetting {
|
||||
@@ -39,7 +41,7 @@ class AppSetting with _$AppSetting {
|
||||
final appSetting =
|
||||
json == null ? defaultAppSetting : AppSetting.fromJson(json);
|
||||
return appSetting.copyWith(
|
||||
isAnimateToPage: system.isDesktop ? false : true,
|
||||
isAnimateToPage: system.isDesktop ? false : appSetting.isAnimateToPage,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -68,7 +70,7 @@ extension AccessControlExt on AccessControl {
|
||||
@freezed
|
||||
class WindowProps with _$WindowProps {
|
||||
const factory WindowProps({
|
||||
@Default(1000) double width,
|
||||
@Default(900) double width,
|
||||
@Default(600) double height,
|
||||
double? top,
|
||||
double? left,
|
||||
@@ -107,7 +109,6 @@ class VpnProps with _$VpnProps {
|
||||
@Default(true) bool systemProxy,
|
||||
@Default(false) bool ipv6,
|
||||
@Default(true) bool allowBypass,
|
||||
@Default(defaultBypassDomain) List<String> bypassDomain,
|
||||
}) = _VpnProps;
|
||||
|
||||
factory VpnProps.fromJson(Map<String, Object?>? json) =>
|
||||
@@ -115,13 +116,14 @@ class VpnProps with _$VpnProps {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DesktopProps with _$DesktopProps {
|
||||
const factory DesktopProps({
|
||||
class NetworkProps with _$NetworkProps {
|
||||
const factory NetworkProps({
|
||||
@Default(true) bool systemProxy,
|
||||
}) = _DesktopProps;
|
||||
@Default(defaultBypassDomain) List<String> bypassDomain,
|
||||
}) = _NetworkProps;
|
||||
|
||||
factory DesktopProps.fromJson(Map<String, Object?>? json) =>
|
||||
json == null ? const DesktopProps() : _$DesktopPropsFromJson(json);
|
||||
factory NetworkProps.fromJson(Map<String, Object?>? json) =>
|
||||
json == null ? const NetworkProps() : _$NetworkPropsFromJson(json);
|
||||
}
|
||||
|
||||
const defaultProxiesStyle = ProxiesStyle();
|
||||
@@ -141,37 +143,39 @@ class ProxiesStyle with _$ProxiesStyle {
|
||||
json == null ? defaultProxiesStyle : _$ProxiesStyleFromJson(json);
|
||||
}
|
||||
|
||||
const defaultThemeProps = ThemeProps();
|
||||
final defaultThemeProps = Platform.isWindows
|
||||
? const ThemeProps().copyWith(
|
||||
fontFamily: FontFamily.miSans,
|
||||
primaryColor: defaultPrimaryColor.value,
|
||||
)
|
||||
: const ThemeProps().copyWith(
|
||||
primaryColor: defaultPrimaryColor.value,
|
||||
);
|
||||
|
||||
@freezed
|
||||
class ThemeProps with _$ThemeProps {
|
||||
const factory ThemeProps({
|
||||
@Default(0xFF795548) int? primaryColor,
|
||||
int? primaryColor,
|
||||
@Default(ThemeMode.system) ThemeMode themeMode,
|
||||
@Default(false) bool prueBlack,
|
||||
@Default(FontFamily.system) FontFamily fontFamily,
|
||||
}) = _ThemeProps;
|
||||
|
||||
factory ThemeProps.fromJson(Map<String, Object?> json) => _$ThemePropsFromJson(json);
|
||||
factory ThemeProps.fromJson(Map<String, Object?> json) =>
|
||||
_$ThemePropsFromJson(json);
|
||||
|
||||
factory ThemeProps.realFromJson(Map<String, Object?>? json) {
|
||||
if (json == null) {
|
||||
return Platform.isWindows
|
||||
? defaultThemeProps.copyWith(fontFamily: FontFamily.miSans)
|
||||
: defaultThemeProps;
|
||||
return defaultThemeProps;
|
||||
}
|
||||
try {
|
||||
return ThemeProps.fromJson(json);
|
||||
} catch (_) {
|
||||
return Platform.isWindows
|
||||
? defaultThemeProps.copyWith(fontFamily: FontFamily.miSans)
|
||||
: defaultThemeProps;
|
||||
return defaultThemeProps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultCustomFontSizeScale = 1.0;
|
||||
|
||||
@JsonSerializable()
|
||||
class Config extends ChangeNotifier {
|
||||
AppSetting _appSetting;
|
||||
@@ -183,7 +187,7 @@ class Config extends ChangeNotifier {
|
||||
WindowProps _windowProps;
|
||||
ThemeProps _themeProps;
|
||||
VpnProps _vpnProps;
|
||||
DesktopProps _desktopProps;
|
||||
NetworkProps _networkProps;
|
||||
bool _overrideDns;
|
||||
List<HotKeyAction> _hotKeyActions;
|
||||
ProxiesStyle _proxiesStyle;
|
||||
@@ -194,7 +198,7 @@ class Config extends ChangeNotifier {
|
||||
_accessControl = const AccessControl(),
|
||||
_windowProps = const WindowProps(),
|
||||
_vpnProps = defaultVpnProps,
|
||||
_desktopProps = const DesktopProps(),
|
||||
_networkProps = const NetworkProps(),
|
||||
_overrideDns = false,
|
||||
_appSetting = defaultAppSetting,
|
||||
_hotKeyActions = [],
|
||||
@@ -379,11 +383,11 @@ class Config extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
DesktopProps get desktopProps => _desktopProps;
|
||||
NetworkProps get networkProps => _networkProps;
|
||||
|
||||
set desktopProps(DesktopProps value) {
|
||||
if (_desktopProps != value) {
|
||||
_desktopProps = value;
|
||||
set networkProps(NetworkProps value) {
|
||||
if (_networkProps != value) {
|
||||
_networkProps = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -466,7 +470,7 @@ class Config extends ChangeNotifier {
|
||||
_proxiesStyle = config._proxiesStyle;
|
||||
_vpnProps = config._vpnProps;
|
||||
_overrideDns = config._overrideDns;
|
||||
_desktopProps = config._desktopProps;
|
||||
_networkProps = config._networkProps;
|
||||
_hotKeyActions = config._hotKeyActions;
|
||||
}
|
||||
notifyListeners();
|
||||
@@ -479,4 +483,9 @@ class Config extends ChangeNotifier {
|
||||
factory Config.fromJson(Map<String, dynamic> json) {
|
||||
return _$ConfigFromJson(json);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Config{_appSetting: $_appSetting, _profiles: $_profiles, _currentProfileId: $_currentProfileId, _isAccessControl: $_isAccessControl, _accessControl: $_accessControl, _dav: $_dav, _windowProps: $_windowProps, _themeProps: $_themeProps, _vpnProps: $_vpnProps, _networkProps: $_networkProps, _overrideDns: $_overrideDns, _hotKeyActions: $_hotKeyActions, _proxiesStyle: $_proxiesStyle}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@ import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'generated/ffi.g.dart';
|
||||
|
||||
part 'generated/ffi.freezed.dart';
|
||||
part 'generated/ffi.g.dart';
|
||||
|
||||
@freezed
|
||||
class CoreState with _$CoreState {
|
||||
@@ -17,6 +16,7 @@ class CoreState with _$CoreState {
|
||||
required bool allowBypass,
|
||||
required bool systemProxy,
|
||||
required List<String> bypassDomain,
|
||||
required List<String> routeAddress,
|
||||
required bool ipv6,
|
||||
required bool onlyProxy,
|
||||
}) = _CoreState;
|
||||
@@ -36,6 +36,7 @@ class AndroidVpnOptions with _$AndroidVpnOptions {
|
||||
required List<String> bypassDomain,
|
||||
required String ipv4Address,
|
||||
required String ipv6Address,
|
||||
required List<String> routeAddress,
|
||||
required String dnsServerAddress,
|
||||
}) = _AndroidVpnOptions;
|
||||
|
||||
@@ -154,13 +155,38 @@ class ProcessMapItem with _$ProcessMapItem {
|
||||
_$ProcessMapItemFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProviderSubscriptionInfo with _$ProviderSubscriptionInfo {
|
||||
const factory ProviderSubscriptionInfo({
|
||||
@JsonKey(name: "UPLOAD") @Default(0) int upload,
|
||||
@JsonKey(name: "DOWNLOAD") @Default(0) int download,
|
||||
@JsonKey(name: "TOTAL") @Default(0) int total,
|
||||
@JsonKey(name: "EXPIRE") @Default(0) int expire,
|
||||
}) = _ProviderSubscriptionInfo;
|
||||
|
||||
factory ProviderSubscriptionInfo.fromJson(Map<String, Object?> json) =>
|
||||
_$ProviderSubscriptionInfoFromJson(json);
|
||||
}
|
||||
|
||||
SubscriptionInfo? subscriptionInfoFormCore(Map<String, Object?>? json) {
|
||||
if (json == null) return null;
|
||||
return SubscriptionInfo(
|
||||
upload: (json['Upload'] as num?)?.toInt() ?? 0,
|
||||
download: (json['Download'] as num?)?.toInt() ?? 0,
|
||||
total: (json['Total'] as num?)?.toInt() ?? 0,
|
||||
expire: (json['Expire'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ExternalProvider with _$ExternalProvider {
|
||||
const factory ExternalProvider({
|
||||
required String name,
|
||||
required String type,
|
||||
required String path,
|
||||
String? path,
|
||||
required int count,
|
||||
@JsonKey(name: "subscription-info", fromJson: subscriptionInfoFormCore)
|
||||
SubscriptionInfo? subscriptionInfo,
|
||||
@Default(false) bool isUpdating,
|
||||
@JsonKey(name: "vehicle-type") required String vehicleType,
|
||||
@JsonKey(name: "update-at") required DateTime updateAt,
|
||||
|
||||
@@ -26,8 +26,12 @@ mixin _$Tun {
|
||||
@JsonKey(name: "dns-hijack")
|
||||
List<String> get dnsHijack => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Tun to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Tun
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$TunCopyWith<Tun> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -52,6 +56,8 @@ class _$TunCopyWithImpl<$Res, $Val extends Tun> implements $TunCopyWith<$Res> {
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Tun
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -100,6 +106,8 @@ class __$$TunImplCopyWithImpl<$Res> extends _$TunCopyWithImpl<$Res, _$TunImpl>
|
||||
__$$TunImplCopyWithImpl(_$TunImpl _value, $Res Function(_$TunImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Tun
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -178,12 +186,14 @@ class _$TunImpl implements _Tun {
|
||||
.equals(other._dnsHijack, _dnsHijack));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, enable, device, stack,
|
||||
const DeepCollectionEquality().hash(_dnsHijack));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Tun
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$TunImplCopyWith<_$TunImpl> get copyWith =>
|
||||
@@ -215,8 +225,11 @@ abstract class _Tun implements Tun {
|
||||
@override
|
||||
@JsonKey(name: "dns-hijack")
|
||||
List<String> get dnsHijack;
|
||||
|
||||
/// Create a copy of Tun
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$TunImplCopyWith<_$TunImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -234,8 +247,12 @@ mixin _$FallbackFilter {
|
||||
List<String> get ipcidr => throw _privateConstructorUsedError;
|
||||
List<String> get domain => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this FallbackFilter to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of FallbackFilter
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$FallbackFilterCopyWith<FallbackFilter> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -264,6 +281,8 @@ class _$FallbackFilterCopyWithImpl<$Res, $Val extends FallbackFilter>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of FallbackFilter
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -322,6 +341,8 @@ class __$$FallbackFilterImplCopyWithImpl<$Res>
|
||||
_$FallbackFilterImpl _value, $Res Function(_$FallbackFilterImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of FallbackFilter
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -427,7 +448,7 @@ class _$FallbackFilterImpl implements _FallbackFilter {
|
||||
const DeepCollectionEquality().equals(other._domain, _domain));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
@@ -437,7 +458,9 @@ class _$FallbackFilterImpl implements _FallbackFilter {
|
||||
const DeepCollectionEquality().hash(_ipcidr),
|
||||
const DeepCollectionEquality().hash(_domain));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of FallbackFilter
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FallbackFilterImplCopyWith<_$FallbackFilterImpl> get copyWith =>
|
||||
@@ -474,8 +497,11 @@ abstract class _FallbackFilter implements FallbackFilter {
|
||||
List<String> get ipcidr;
|
||||
@override
|
||||
List<String> get domain;
|
||||
|
||||
/// Create a copy of FallbackFilter
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$FallbackFilterImplCopyWith<_$FallbackFilterImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -514,8 +540,12 @@ mixin _$Dns {
|
||||
@JsonKey(name: "fallback-filter")
|
||||
FallbackFilter get fallbackFilter => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Dns to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Dns
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$DnsCopyWith<Dns> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -554,6 +584,8 @@ class _$DnsCopyWithImpl<$Res, $Val extends Dns> implements $DnsCopyWith<$Res> {
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Dns
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -637,6 +669,8 @@ class _$DnsCopyWithImpl<$Res, $Val extends Dns> implements $DnsCopyWith<$Res> {
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of Dns
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$FallbackFilterCopyWith<$Res> get fallbackFilter {
|
||||
@@ -680,6 +714,8 @@ class __$$DnsImplCopyWithImpl<$Res> extends _$DnsCopyWithImpl<$Res, _$DnsImpl>
|
||||
__$$DnsImplCopyWithImpl(_$DnsImpl _value, $Res Function(_$DnsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Dns
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -932,7 +968,7 @@ class _$DnsImpl implements _Dns {
|
||||
other.fallbackFilter == fallbackFilter));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
@@ -952,7 +988,9 @@ class _$DnsImpl implements _Dns {
|
||||
const DeepCollectionEquality().hash(_proxyServerNameserver),
|
||||
fallbackFilter);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Dns
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$DnsImplCopyWith<_$DnsImpl> get copyWith =>
|
||||
@@ -1030,8 +1068,11 @@ abstract class _Dns implements Dns {
|
||||
@override
|
||||
@JsonKey(name: "fallback-filter")
|
||||
FallbackFilter get fallbackFilter;
|
||||
|
||||
/// Create a copy of Dns
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$DnsImplCopyWith<_$DnsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,13 @@ ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => ClashConfig()
|
||||
..hosts = (json['hosts'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
{};
|
||||
{}
|
||||
..includeRouteAddress = (json['include-route-address'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
[]
|
||||
..routeMode = $enumDecodeNullable(_$RouteModeEnumMap, json['route-mode']) ??
|
||||
RouteMode.config;
|
||||
|
||||
Map<String, dynamic> _$ClashConfigToJson(ClashConfig instance) =>
|
||||
<String, dynamic>{
|
||||
@@ -63,6 +69,9 @@ Map<String, dynamic> _$ClashConfigToJson(ClashConfig instance) =>
|
||||
'global-real-ua': instance.globalRealUa,
|
||||
'geox-url': instance.geoXUrl,
|
||||
'hosts': instance.hosts,
|
||||
'route-address': instance.routeAddress,
|
||||
'include-route-address': instance.includeRouteAddress,
|
||||
'route-mode': _$RouteModeEnumMap[instance.routeMode]!,
|
||||
};
|
||||
|
||||
const _$ModeEnumMap = {
|
||||
@@ -84,6 +93,11 @@ const _$LogLevelEnumMap = {
|
||||
LogLevel.silent: 'silent',
|
||||
};
|
||||
|
||||
const _$RouteModeEnumMap = {
|
||||
RouteMode.bypassPrivate: 'bypassPrivate',
|
||||
RouteMode.config: 'config',
|
||||
};
|
||||
|
||||
_$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
device: json['device'] as String? ?? appName,
|
||||
|
||||
@@ -24,7 +24,9 @@ mixin _$NavigationItem {
|
||||
String? get path => throw _privateConstructorUsedError;
|
||||
List<NavigationItemMode> get modes => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of NavigationItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$NavigationItemCopyWith<NavigationItem> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -55,6 +57,8 @@ class _$NavigationItemCopyWithImpl<$Res, $Val extends NavigationItem>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of NavigationItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -125,6 +129,8 @@ class __$$NavigationItemImplCopyWithImpl<$Res>
|
||||
_$NavigationItemImpl _value, $Res Function(_$NavigationItemImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of NavigationItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -232,7 +238,9 @@ class _$NavigationItemImpl implements _NavigationItem {
|
||||
int get hashCode => Object.hash(runtimeType, icon, label, description,
|
||||
fragment, keep, path, const DeepCollectionEquality().hash(_modes));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of NavigationItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$NavigationItemImplCopyWith<_$NavigationItemImpl> get copyWith =>
|
||||
@@ -264,8 +272,11 @@ abstract class _NavigationItem implements NavigationItem {
|
||||
String? get path;
|
||||
@override
|
||||
List<NavigationItemMode> get modes;
|
||||
|
||||
/// Create a copy of NavigationItem
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$NavigationItemImplCopyWith<_$NavigationItemImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -281,8 +292,12 @@ mixin _$Package {
|
||||
bool get isSystem => throw _privateConstructorUsedError;
|
||||
int get firstInstallTime => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Package to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Package
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$PackageCopyWith<Package> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -305,6 +320,8 @@ class _$PackageCopyWithImpl<$Res, $Val extends Package>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Package
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -353,6 +370,8 @@ class __$$PackageImplCopyWithImpl<$Res>
|
||||
_$PackageImpl _value, $Res Function(_$PackageImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Package
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -422,12 +441,14 @@ class _$PackageImpl implements _Package {
|
||||
other.firstInstallTime == firstInstallTime));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, packageName, label, isSystem, firstInstallTime);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Package
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$PackageImplCopyWith<_$PackageImpl> get copyWith =>
|
||||
@@ -458,8 +479,11 @@ abstract class _Package implements Package {
|
||||
bool get isSystem;
|
||||
@override
|
||||
int get firstInstallTime;
|
||||
|
||||
/// Create a copy of Package
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$PackageImplCopyWith<_$PackageImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -480,8 +504,12 @@ mixin _$Metadata {
|
||||
String get process => throw _privateConstructorUsedError;
|
||||
String get remoteDestination => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Metadata to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Metadata
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$MetadataCopyWith<Metadata> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -513,6 +541,8 @@ class _$MetadataCopyWithImpl<$Res, $Val extends Metadata>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Metadata
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -595,6 +625,8 @@ class __$$MetadataImplCopyWithImpl<$Res>
|
||||
_$MetadataImpl _value, $Res Function(_$MetadataImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Metadata
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -711,7 +743,7 @@ class _$MetadataImpl implements _Metadata {
|
||||
other.remoteDestination == remoteDestination));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
@@ -725,7 +757,9 @@ class _$MetadataImpl implements _Metadata {
|
||||
process,
|
||||
remoteDestination);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Metadata
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$MetadataImplCopyWith<_$MetadataImpl> get copyWith =>
|
||||
@@ -772,8 +806,11 @@ abstract class _Metadata implements Metadata {
|
||||
String get process;
|
||||
@override
|
||||
String get remoteDestination;
|
||||
|
||||
/// Create a copy of Metadata
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$MetadataImplCopyWith<_$MetadataImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -791,8 +828,12 @@ mixin _$Connection {
|
||||
Metadata get metadata => throw _privateConstructorUsedError;
|
||||
List<String> get chains => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Connection to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Connection
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ConnectionCopyWith<Connection> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -824,6 +865,8 @@ class _$ConnectionCopyWithImpl<$Res, $Val extends Connection>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Connection
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -862,6 +905,8 @@ class _$ConnectionCopyWithImpl<$Res, $Val extends Connection>
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of Connection
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$MetadataCopyWith<$Res> get metadata {
|
||||
@@ -899,6 +944,8 @@ class __$$ConnectionImplCopyWithImpl<$Res>
|
||||
_$ConnectionImpl _value, $Res Function(_$ConnectionImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Connection
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -991,12 +1038,14 @@ class _$ConnectionImpl implements _Connection {
|
||||
const DeepCollectionEquality().equals(other._chains, _chains));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id, upload, download, start,
|
||||
metadata, const DeepCollectionEquality().hash(_chains));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Connection
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ConnectionImplCopyWith<_$ConnectionImpl> get copyWith =>
|
||||
@@ -1034,8 +1083,11 @@ abstract class _Connection implements Connection {
|
||||
Metadata get metadata;
|
||||
@override
|
||||
List<String> get chains;
|
||||
|
||||
/// Create a copy of Connection
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ConnectionImplCopyWith<_$ConnectionImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1049,8 +1101,12 @@ mixin _$LogsAndKeywords {
|
||||
List<Log> get logs => throw _privateConstructorUsedError;
|
||||
List<String> get keywords => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this LogsAndKeywords to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of LogsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$LogsAndKeywordsCopyWith<LogsAndKeywords> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1074,6 +1130,8 @@ class _$LogsAndKeywordsCopyWithImpl<$Res, $Val extends LogsAndKeywords>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of LogsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1112,6 +1170,8 @@ class __$$LogsAndKeywordsImplCopyWithImpl<$Res>
|
||||
_$LogsAndKeywordsImpl _value, $Res Function(_$LogsAndKeywordsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of LogsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1174,14 +1234,16 @@ class _$LogsAndKeywordsImpl implements _LogsAndKeywords {
|
||||
const DeepCollectionEquality().equals(other._keywords, _keywords));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_logs),
|
||||
const DeepCollectionEquality().hash(_keywords));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of LogsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$LogsAndKeywordsImplCopyWith<_$LogsAndKeywordsImpl> get copyWith =>
|
||||
@@ -1208,8 +1270,11 @@ abstract class _LogsAndKeywords implements LogsAndKeywords {
|
||||
List<Log> get logs;
|
||||
@override
|
||||
List<String> get keywords;
|
||||
|
||||
/// Create a copy of LogsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$LogsAndKeywordsImplCopyWith<_$LogsAndKeywordsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1224,8 +1289,12 @@ mixin _$ConnectionsAndKeywords {
|
||||
List<Connection> get connections => throw _privateConstructorUsedError;
|
||||
List<String> get keywords => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ConnectionsAndKeywords to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of ConnectionsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ConnectionsAndKeywordsCopyWith<ConnectionsAndKeywords> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1250,6 +1319,8 @@ class _$ConnectionsAndKeywordsCopyWithImpl<$Res,
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ConnectionsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1291,6 +1362,8 @@ class __$$ConnectionsAndKeywordsImplCopyWithImpl<$Res>
|
||||
$Res Function(_$ConnectionsAndKeywordsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ConnectionsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1355,14 +1428,16 @@ class _$ConnectionsAndKeywordsImpl implements _ConnectionsAndKeywords {
|
||||
const DeepCollectionEquality().equals(other._keywords, _keywords));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_connections),
|
||||
const DeepCollectionEquality().hash(_keywords));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of ConnectionsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ConnectionsAndKeywordsImplCopyWith<_$ConnectionsAndKeywordsImpl>
|
||||
@@ -1389,8 +1464,11 @@ abstract class _ConnectionsAndKeywords implements ConnectionsAndKeywords {
|
||||
List<Connection> get connections;
|
||||
@override
|
||||
List<String> get keywords;
|
||||
|
||||
/// Create a copy of ConnectionsAndKeywords
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ConnectionsAndKeywordsImplCopyWith<_$ConnectionsAndKeywordsImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1406,8 +1484,12 @@ mixin _$DAV {
|
||||
String get password => throw _privateConstructorUsedError;
|
||||
String get fileName => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this DAV to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of DAV
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$DAVCopyWith<DAV> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -1428,6 +1510,8 @@ class _$DAVCopyWithImpl<$Res, $Val extends DAV> implements $DAVCopyWith<$Res> {
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of DAV
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1472,6 +1556,8 @@ class __$$DAVImplCopyWithImpl<$Res> extends _$DAVCopyWithImpl<$Res, _$DAVImpl>
|
||||
__$$DAVImplCopyWithImpl(_$DAVImpl _value, $Res Function(_$DAVImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of DAV
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1541,11 +1627,13 @@ class _$DAVImpl implements _DAV {
|
||||
other.fileName == fileName));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, uri, user, password, fileName);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of DAV
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$DAVImplCopyWith<_$DAVImpl> get copyWith =>
|
||||
@@ -1576,8 +1664,11 @@ abstract class _DAV implements DAV {
|
||||
String get password;
|
||||
@override
|
||||
String get fileName;
|
||||
|
||||
/// Create a copy of DAV
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$DAVImplCopyWith<_$DAVImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1587,7 +1678,9 @@ mixin _$FileInfo {
|
||||
int get size => throw _privateConstructorUsedError;
|
||||
DateTime get lastModified => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of FileInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$FileInfoCopyWith<FileInfo> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1610,6 +1703,8 @@ class _$FileInfoCopyWithImpl<$Res, $Val extends FileInfo>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of FileInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1648,6 +1743,8 @@ class __$$FileInfoImplCopyWithImpl<$Res>
|
||||
_$FileInfoImpl _value, $Res Function(_$FileInfoImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of FileInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1695,7 +1792,9 @@ class _$FileInfoImpl implements _FileInfo {
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, size, lastModified);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of FileInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FileInfoImplCopyWith<_$FileInfoImpl> get copyWith =>
|
||||
@@ -1711,8 +1810,11 @@ abstract class _FileInfo implements FileInfo {
|
||||
int get size;
|
||||
@override
|
||||
DateTime get lastModified;
|
||||
|
||||
/// Create a copy of FileInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$FileInfoImplCopyWith<_$FileInfoImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1726,8 +1828,12 @@ mixin _$VersionInfo {
|
||||
String get clashName => throw _privateConstructorUsedError;
|
||||
String get version => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this VersionInfo to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of VersionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$VersionInfoCopyWith<VersionInfo> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1751,6 +1857,8 @@ class _$VersionInfoCopyWithImpl<$Res, $Val extends VersionInfo>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of VersionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1789,6 +1897,8 @@ class __$$VersionInfoImplCopyWithImpl<$Res>
|
||||
_$VersionInfoImpl _value, $Res Function(_$VersionInfoImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of VersionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1838,11 +1948,13 @@ class _$VersionInfoImpl implements _VersionInfo {
|
||||
(identical(other.version, version) || other.version == version));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, clashName, version);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of VersionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$VersionInfoImplCopyWith<_$VersionInfoImpl> get copyWith =>
|
||||
@@ -1867,8 +1979,11 @@ abstract class _VersionInfo implements VersionInfo {
|
||||
String get clashName;
|
||||
@override
|
||||
String get version;
|
||||
|
||||
/// Create a copy of VersionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$VersionInfoImplCopyWith<_$VersionInfoImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1886,8 +2001,12 @@ mixin _$Group {
|
||||
String get icon => throw _privateConstructorUsedError;
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Group to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Group
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$GroupCopyWith<Group> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -1915,6 +2034,8 @@ class _$GroupCopyWithImpl<$Res, $Val extends Group>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Group
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1978,6 +2099,8 @@ class __$$GroupImplCopyWithImpl<$Res>
|
||||
_$GroupImpl _value, $Res Function(_$GroupImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Group
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -2071,12 +2194,14 @@ class _$GroupImpl implements _Group {
|
||||
(identical(other.name, name) || other.name == name));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, type,
|
||||
const DeepCollectionEquality().hash(_all), now, hidden, icon, name);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Group
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$GroupImplCopyWith<_$GroupImpl> get copyWith =>
|
||||
@@ -2113,8 +2238,11 @@ abstract class _Group implements Group {
|
||||
String get icon;
|
||||
@override
|
||||
String get name;
|
||||
|
||||
/// Create a copy of Group
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$GroupImplCopyWith<_$GroupImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -2129,8 +2257,12 @@ mixin _$Proxy {
|
||||
String get type => throw _privateConstructorUsedError;
|
||||
String? get now => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Proxy to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ProxyCopyWith<Proxy> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -2152,6 +2284,8 @@ class _$ProxyCopyWithImpl<$Res, $Val extends Proxy>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -2194,6 +2328,8 @@ class __$$ProxyImplCopyWithImpl<$Res>
|
||||
_$ProxyImpl _value, $Res Function(_$ProxyImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -2248,11 +2384,13 @@ class _$ProxyImpl implements _Proxy {
|
||||
(identical(other.now, now) || other.now == now));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, name, type, now);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProxyImplCopyWith<_$ProxyImpl> get copyWith =>
|
||||
@@ -2280,8 +2418,11 @@ abstract class _Proxy implements Proxy {
|
||||
String get type;
|
||||
@override
|
||||
String? get now;
|
||||
|
||||
/// Create a copy of Proxy
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ProxyImplCopyWith<_$ProxyImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -2296,8 +2437,12 @@ mixin _$HotKeyAction {
|
||||
int? get key => throw _privateConstructorUsedError;
|
||||
Set<KeyboardModifier> get modifiers => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this HotKeyAction to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of HotKeyAction
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$HotKeyActionCopyWith<HotKeyAction> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -2321,6 +2466,8 @@ class _$HotKeyActionCopyWithImpl<$Res, $Val extends HotKeyAction>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of HotKeyAction
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -2364,6 +2511,8 @@ class __$$HotKeyActionImplCopyWithImpl<$Res>
|
||||
_$HotKeyActionImpl _value, $Res Function(_$HotKeyActionImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of HotKeyAction
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -2429,12 +2578,14 @@ class _$HotKeyActionImpl implements _HotKeyAction {
|
||||
.equals(other._modifiers, _modifiers));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, action, key,
|
||||
const DeepCollectionEquality().hash(_modifiers));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of HotKeyAction
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$HotKeyActionImplCopyWith<_$HotKeyActionImpl> get copyWith =>
|
||||
@@ -2463,8 +2614,11 @@ abstract class _HotKeyAction implements HotKeyAction {
|
||||
int? get key;
|
||||
@override
|
||||
Set<KeyboardModifier> get modifiers;
|
||||
|
||||
/// Create a copy of HotKeyAction
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$HotKeyActionImplCopyWith<_$HotKeyActionImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -2475,7 +2629,9 @@ mixin _$Field {
|
||||
String get value => throw _privateConstructorUsedError;
|
||||
Validator? get validator => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Field
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$FieldCopyWith<Field> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -2497,6 +2653,8 @@ class _$FieldCopyWithImpl<$Res, $Val extends Field>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Field
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -2539,6 +2697,8 @@ class __$$FieldImplCopyWithImpl<$Res>
|
||||
_$FieldImpl _value, $Res Function(_$FieldImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Field
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -2594,7 +2754,9 @@ class _$FieldImpl implements _Field {
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, label, value, validator);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Field
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FieldImplCopyWith<_$FieldImpl> get copyWith =>
|
||||
@@ -2613,8 +2775,11 @@ abstract class _Field implements Field {
|
||||
String get value;
|
||||
@override
|
||||
Validator? get validator;
|
||||
|
||||
/// Create a copy of Field
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$FieldImplCopyWith<_$FieldImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,12 @@ mixin _$AppSetting {
|
||||
bool get minimizeOnExit => throw _privateConstructorUsedError;
|
||||
bool get hidden => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this AppSetting to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of AppSetting
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$AppSettingCopyWith<AppSetting> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -76,6 +80,8 @@ class _$AppSettingCopyWithImpl<$Res, $Val extends AppSetting>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of AppSetting
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -194,6 +200,8 @@ class __$$AppSettingImplCopyWithImpl<$Res>
|
||||
_$AppSettingImpl _value, $Res Function(_$AppSettingImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of AppSetting
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -384,7 +392,7 @@ class _$AppSettingImpl implements _AppSetting {
|
||||
(identical(other.hidden, hidden) || other.hidden == hidden));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
@@ -404,7 +412,9 @@ class _$AppSettingImpl implements _AppSetting {
|
||||
minimizeOnExit,
|
||||
hidden);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of AppSetting
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$AppSettingImplCopyWith<_$AppSettingImpl> get copyWith =>
|
||||
@@ -469,8 +479,11 @@ abstract class _AppSetting implements AppSetting {
|
||||
bool get minimizeOnExit;
|
||||
@override
|
||||
bool get hidden;
|
||||
|
||||
/// Create a copy of AppSetting
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$AppSettingImplCopyWith<_$AppSettingImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -487,8 +500,12 @@ mixin _$AccessControl {
|
||||
AccessSortType get sort => throw _privateConstructorUsedError;
|
||||
bool get isFilterSystemApp => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this AccessControl to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of AccessControl
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$AccessControlCopyWith<AccessControl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -517,6 +534,8 @@ class _$AccessControlCopyWithImpl<$Res, $Val extends AccessControl>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of AccessControl
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -575,6 +594,8 @@ class __$$AccessControlImplCopyWithImpl<$Res>
|
||||
_$AccessControlImpl _value, $Res Function(_$AccessControlImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of AccessControl
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -672,7 +693,7 @@ class _$AccessControlImpl implements _AccessControl {
|
||||
other.isFilterSystemApp == isFilterSystemApp));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
@@ -682,7 +703,9 @@ class _$AccessControlImpl implements _AccessControl {
|
||||
sort,
|
||||
isFilterSystemApp);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of AccessControl
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$AccessControlImplCopyWith<_$AccessControlImpl> get copyWith =>
|
||||
@@ -717,8 +740,11 @@ abstract class _AccessControl implements AccessControl {
|
||||
AccessSortType get sort;
|
||||
@override
|
||||
bool get isFilterSystemApp;
|
||||
|
||||
/// Create a copy of AccessControl
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$AccessControlImplCopyWith<_$AccessControlImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -734,8 +760,12 @@ mixin _$WindowProps {
|
||||
double? get top => throw _privateConstructorUsedError;
|
||||
double? get left => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this WindowProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of WindowProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$WindowPropsCopyWith<WindowProps> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -759,6 +789,8 @@ class _$WindowPropsCopyWithImpl<$Res, $Val extends WindowProps>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of WindowProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -807,6 +839,8 @@ class __$$WindowPropsImplCopyWithImpl<$Res>
|
||||
_$WindowPropsImpl _value, $Res Function(_$WindowPropsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of WindowProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -840,7 +874,7 @@ class __$$WindowPropsImplCopyWithImpl<$Res>
|
||||
@JsonSerializable()
|
||||
class _$WindowPropsImpl implements _WindowProps {
|
||||
const _$WindowPropsImpl(
|
||||
{this.width = 1000, this.height = 600, this.top, this.left});
|
||||
{this.width = 900, this.height = 600, this.top, this.left});
|
||||
|
||||
factory _$WindowPropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$WindowPropsImplFromJson(json);
|
||||
@@ -872,11 +906,13 @@ class _$WindowPropsImpl implements _WindowProps {
|
||||
(identical(other.left, left) || other.left == left));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, width, height, top, left);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of WindowProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$WindowPropsImplCopyWith<_$WindowPropsImpl> get copyWith =>
|
||||
@@ -908,8 +944,11 @@ abstract class _WindowProps implements WindowProps {
|
||||
double? get top;
|
||||
@override
|
||||
double? get left;
|
||||
|
||||
/// Create a copy of WindowProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$WindowPropsImplCopyWith<_$WindowPropsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -924,10 +963,13 @@ mixin _$VpnProps {
|
||||
bool get systemProxy => throw _privateConstructorUsedError;
|
||||
bool get ipv6 => throw _privateConstructorUsedError;
|
||||
bool get allowBypass => throw _privateConstructorUsedError;
|
||||
List<String> get bypassDomain => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this VpnProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of VpnProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$VpnPropsCopyWith<VpnProps> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -937,12 +979,7 @@ abstract class $VpnPropsCopyWith<$Res> {
|
||||
factory $VpnPropsCopyWith(VpnProps value, $Res Function(VpnProps) then) =
|
||||
_$VpnPropsCopyWithImpl<$Res, VpnProps>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool enable,
|
||||
bool systemProxy,
|
||||
bool ipv6,
|
||||
bool allowBypass,
|
||||
List<String> bypassDomain});
|
||||
$Res call({bool enable, bool systemProxy, bool ipv6, bool allowBypass});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -955,6 +992,8 @@ class _$VpnPropsCopyWithImpl<$Res, $Val extends VpnProps>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of VpnProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -962,7 +1001,6 @@ class _$VpnPropsCopyWithImpl<$Res, $Val extends VpnProps>
|
||||
Object? systemProxy = null,
|
||||
Object? ipv6 = null,
|
||||
Object? allowBypass = null,
|
||||
Object? bypassDomain = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
enable: null == enable
|
||||
@@ -981,10 +1019,6 @@ class _$VpnPropsCopyWithImpl<$Res, $Val extends VpnProps>
|
||||
? _value.allowBypass
|
||||
: allowBypass // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
bypassDomain: null == bypassDomain
|
||||
? _value.bypassDomain
|
||||
: bypassDomain // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -997,12 +1031,7 @@ abstract class _$$VpnPropsImplCopyWith<$Res>
|
||||
__$$VpnPropsImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool enable,
|
||||
bool systemProxy,
|
||||
bool ipv6,
|
||||
bool allowBypass,
|
||||
List<String> bypassDomain});
|
||||
$Res call({bool enable, bool systemProxy, bool ipv6, bool allowBypass});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1013,6 +1042,8 @@ class __$$VpnPropsImplCopyWithImpl<$Res>
|
||||
_$VpnPropsImpl _value, $Res Function(_$VpnPropsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of VpnProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1020,7 +1051,6 @@ class __$$VpnPropsImplCopyWithImpl<$Res>
|
||||
Object? systemProxy = null,
|
||||
Object? ipv6 = null,
|
||||
Object? allowBypass = null,
|
||||
Object? bypassDomain = null,
|
||||
}) {
|
||||
return _then(_$VpnPropsImpl(
|
||||
enable: null == enable
|
||||
@@ -1039,10 +1069,6 @@ class __$$VpnPropsImplCopyWithImpl<$Res>
|
||||
? _value.allowBypass
|
||||
: allowBypass // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
bypassDomain: null == bypassDomain
|
||||
? _value._bypassDomain
|
||||
: bypassDomain // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1054,9 +1080,7 @@ class _$VpnPropsImpl implements _VpnProps {
|
||||
{this.enable = true,
|
||||
this.systemProxy = true,
|
||||
this.ipv6 = false,
|
||||
this.allowBypass = true,
|
||||
final List<String> bypassDomain = defaultBypassDomain})
|
||||
: _bypassDomain = bypassDomain;
|
||||
this.allowBypass = true});
|
||||
|
||||
factory _$VpnPropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$VpnPropsImplFromJson(json);
|
||||
@@ -1073,18 +1097,10 @@ class _$VpnPropsImpl implements _VpnProps {
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool allowBypass;
|
||||
final List<String> _bypassDomain;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<String> get bypassDomain {
|
||||
if (_bypassDomain is EqualUnmodifiableListView) return _bypassDomain;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_bypassDomain);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'VpnProps(enable: $enable, systemProxy: $systemProxy, ipv6: $ipv6, allowBypass: $allowBypass, bypassDomain: $bypassDomain)';
|
||||
return 'VpnProps(enable: $enable, systemProxy: $systemProxy, ipv6: $ipv6, allowBypass: $allowBypass)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1097,17 +1113,17 @@ class _$VpnPropsImpl implements _VpnProps {
|
||||
other.systemProxy == systemProxy) &&
|
||||
(identical(other.ipv6, ipv6) || other.ipv6 == ipv6) &&
|
||||
(identical(other.allowBypass, allowBypass) ||
|
||||
other.allowBypass == allowBypass) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._bypassDomain, _bypassDomain));
|
||||
other.allowBypass == allowBypass));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, enable, systemProxy, ipv6,
|
||||
allowBypass, const DeepCollectionEquality().hash(_bypassDomain));
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, enable, systemProxy, ipv6, allowBypass);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of VpnProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$VpnPropsImplCopyWith<_$VpnPropsImpl> get copyWith =>
|
||||
@@ -1126,8 +1142,7 @@ abstract class _VpnProps implements VpnProps {
|
||||
{final bool enable,
|
||||
final bool systemProxy,
|
||||
final bool ipv6,
|
||||
final bool allowBypass,
|
||||
final List<String> bypassDomain}) = _$VpnPropsImpl;
|
||||
final bool allowBypass}) = _$VpnPropsImpl;
|
||||
|
||||
factory _VpnProps.fromJson(Map<String, dynamic> json) =
|
||||
_$VpnPropsImpl.fromJson;
|
||||
@@ -1140,149 +1155,192 @@ abstract class _VpnProps implements VpnProps {
|
||||
bool get ipv6;
|
||||
@override
|
||||
bool get allowBypass;
|
||||
|
||||
/// Create a copy of VpnProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
List<String> get bypassDomain;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$VpnPropsImplCopyWith<_$VpnPropsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
DesktopProps _$DesktopPropsFromJson(Map<String, dynamic> json) {
|
||||
return _DesktopProps.fromJson(json);
|
||||
NetworkProps _$NetworkPropsFromJson(Map<String, dynamic> json) {
|
||||
return _NetworkProps.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$DesktopProps {
|
||||
mixin _$NetworkProps {
|
||||
bool get systemProxy => throw _privateConstructorUsedError;
|
||||
List<String> get bypassDomain => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this NetworkProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$DesktopPropsCopyWith<DesktopProps> get copyWith =>
|
||||
|
||||
/// Create a copy of NetworkProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$NetworkPropsCopyWith<NetworkProps> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $DesktopPropsCopyWith<$Res> {
|
||||
factory $DesktopPropsCopyWith(
|
||||
DesktopProps value, $Res Function(DesktopProps) then) =
|
||||
_$DesktopPropsCopyWithImpl<$Res, DesktopProps>;
|
||||
abstract class $NetworkPropsCopyWith<$Res> {
|
||||
factory $NetworkPropsCopyWith(
|
||||
NetworkProps value, $Res Function(NetworkProps) then) =
|
||||
_$NetworkPropsCopyWithImpl<$Res, NetworkProps>;
|
||||
@useResult
|
||||
$Res call({bool systemProxy});
|
||||
$Res call({bool systemProxy, List<String> bypassDomain});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$DesktopPropsCopyWithImpl<$Res, $Val extends DesktopProps>
|
||||
implements $DesktopPropsCopyWith<$Res> {
|
||||
_$DesktopPropsCopyWithImpl(this._value, this._then);
|
||||
class _$NetworkPropsCopyWithImpl<$Res, $Val extends NetworkProps>
|
||||
implements $NetworkPropsCopyWith<$Res> {
|
||||
_$NetworkPropsCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of NetworkProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? systemProxy = null,
|
||||
Object? bypassDomain = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
systemProxy: null == systemProxy
|
||||
? _value.systemProxy
|
||||
: systemProxy // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
bypassDomain: null == bypassDomain
|
||||
? _value.bypassDomain
|
||||
: bypassDomain // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$DesktopPropsImplCopyWith<$Res>
|
||||
implements $DesktopPropsCopyWith<$Res> {
|
||||
factory _$$DesktopPropsImplCopyWith(
|
||||
_$DesktopPropsImpl value, $Res Function(_$DesktopPropsImpl) then) =
|
||||
__$$DesktopPropsImplCopyWithImpl<$Res>;
|
||||
abstract class _$$NetworkPropsImplCopyWith<$Res>
|
||||
implements $NetworkPropsCopyWith<$Res> {
|
||||
factory _$$NetworkPropsImplCopyWith(
|
||||
_$NetworkPropsImpl value, $Res Function(_$NetworkPropsImpl) then) =
|
||||
__$$NetworkPropsImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool systemProxy});
|
||||
$Res call({bool systemProxy, List<String> bypassDomain});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$DesktopPropsImplCopyWithImpl<$Res>
|
||||
extends _$DesktopPropsCopyWithImpl<$Res, _$DesktopPropsImpl>
|
||||
implements _$$DesktopPropsImplCopyWith<$Res> {
|
||||
__$$DesktopPropsImplCopyWithImpl(
|
||||
_$DesktopPropsImpl _value, $Res Function(_$DesktopPropsImpl) _then)
|
||||
class __$$NetworkPropsImplCopyWithImpl<$Res>
|
||||
extends _$NetworkPropsCopyWithImpl<$Res, _$NetworkPropsImpl>
|
||||
implements _$$NetworkPropsImplCopyWith<$Res> {
|
||||
__$$NetworkPropsImplCopyWithImpl(
|
||||
_$NetworkPropsImpl _value, $Res Function(_$NetworkPropsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of NetworkProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? systemProxy = null,
|
||||
Object? bypassDomain = null,
|
||||
}) {
|
||||
return _then(_$DesktopPropsImpl(
|
||||
return _then(_$NetworkPropsImpl(
|
||||
systemProxy: null == systemProxy
|
||||
? _value.systemProxy
|
||||
: systemProxy // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
bypassDomain: null == bypassDomain
|
||||
? _value._bypassDomain
|
||||
: bypassDomain // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$DesktopPropsImpl implements _DesktopProps {
|
||||
const _$DesktopPropsImpl({this.systemProxy = true});
|
||||
class _$NetworkPropsImpl implements _NetworkProps {
|
||||
const _$NetworkPropsImpl(
|
||||
{this.systemProxy = true,
|
||||
final List<String> bypassDomain = defaultBypassDomain})
|
||||
: _bypassDomain = bypassDomain;
|
||||
|
||||
factory _$DesktopPropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$DesktopPropsImplFromJson(json);
|
||||
factory _$NetworkPropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$NetworkPropsImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool systemProxy;
|
||||
final List<String> _bypassDomain;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<String> get bypassDomain {
|
||||
if (_bypassDomain is EqualUnmodifiableListView) return _bypassDomain;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_bypassDomain);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DesktopProps(systemProxy: $systemProxy)';
|
||||
return 'NetworkProps(systemProxy: $systemProxy, bypassDomain: $bypassDomain)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$DesktopPropsImpl &&
|
||||
other is _$NetworkPropsImpl &&
|
||||
(identical(other.systemProxy, systemProxy) ||
|
||||
other.systemProxy == systemProxy));
|
||||
other.systemProxy == systemProxy) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._bypassDomain, _bypassDomain));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, systemProxy);
|
||||
int get hashCode => Object.hash(runtimeType, systemProxy,
|
||||
const DeepCollectionEquality().hash(_bypassDomain));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of NetworkProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$DesktopPropsImplCopyWith<_$DesktopPropsImpl> get copyWith =>
|
||||
__$$DesktopPropsImplCopyWithImpl<_$DesktopPropsImpl>(this, _$identity);
|
||||
_$$NetworkPropsImplCopyWith<_$NetworkPropsImpl> get copyWith =>
|
||||
__$$NetworkPropsImplCopyWithImpl<_$NetworkPropsImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$DesktopPropsImplToJson(
|
||||
return _$$NetworkPropsImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _DesktopProps implements DesktopProps {
|
||||
const factory _DesktopProps({final bool systemProxy}) = _$DesktopPropsImpl;
|
||||
abstract class _NetworkProps implements NetworkProps {
|
||||
const factory _NetworkProps(
|
||||
{final bool systemProxy,
|
||||
final List<String> bypassDomain}) = _$NetworkPropsImpl;
|
||||
|
||||
factory _DesktopProps.fromJson(Map<String, dynamic> json) =
|
||||
_$DesktopPropsImpl.fromJson;
|
||||
factory _NetworkProps.fromJson(Map<String, dynamic> json) =
|
||||
_$NetworkPropsImpl.fromJson;
|
||||
|
||||
@override
|
||||
bool get systemProxy;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$DesktopPropsImplCopyWith<_$DesktopPropsImpl> get copyWith =>
|
||||
List<String> get bypassDomain;
|
||||
|
||||
/// Create a copy of NetworkProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$NetworkPropsImplCopyWith<_$NetworkPropsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -1299,8 +1357,12 @@ mixin _$ProxiesStyle {
|
||||
ProxyCardType get cardType => throw _privateConstructorUsedError;
|
||||
Map<String, String> get iconMap => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ProxiesStyle to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of ProxiesStyle
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ProxiesStyleCopyWith<ProxiesStyle> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1330,6 +1392,8 @@ class _$ProxiesStyleCopyWithImpl<$Res, $Val extends ProxiesStyle>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ProxiesStyle
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1394,6 +1458,8 @@ class __$$ProxiesStyleImplCopyWithImpl<$Res>
|
||||
_$ProxiesStyleImpl _value, $Res Function(_$ProxiesStyleImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ProxiesStyle
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1493,12 +1559,14 @@ class _$ProxiesStyleImpl implements _ProxiesStyle {
|
||||
const DeepCollectionEquality().equals(other._iconMap, _iconMap));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, type, sortType, layout,
|
||||
iconStyle, cardType, const DeepCollectionEquality().hash(_iconMap));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of ProxiesStyle
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProxiesStyleImplCopyWith<_$ProxiesStyleImpl> get copyWith =>
|
||||
@@ -1536,8 +1604,11 @@ abstract class _ProxiesStyle implements ProxiesStyle {
|
||||
ProxyCardType get cardType;
|
||||
@override
|
||||
Map<String, String> get iconMap;
|
||||
|
||||
/// Create a copy of ProxiesStyle
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ProxiesStyleImplCopyWith<_$ProxiesStyleImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1553,8 +1624,12 @@ mixin _$ThemeProps {
|
||||
bool get prueBlack => throw _privateConstructorUsedError;
|
||||
FontFamily get fontFamily => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ThemeProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ThemePropsCopyWith<ThemeProps> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -1582,6 +1657,8 @@ class _$ThemePropsCopyWithImpl<$Res, $Val extends ThemeProps>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1634,6 +1711,8 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
|
||||
_$ThemePropsImpl _value, $Res Function(_$ThemePropsImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -1667,7 +1746,7 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
|
||||
@JsonSerializable()
|
||||
class _$ThemePropsImpl implements _ThemeProps {
|
||||
const _$ThemePropsImpl(
|
||||
{this.primaryColor = 0xFF795548,
|
||||
{this.primaryColor,
|
||||
this.themeMode = ThemeMode.system,
|
||||
this.prueBlack = false,
|
||||
this.fontFamily = FontFamily.system});
|
||||
@@ -1676,7 +1755,6 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
_$$ThemePropsImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final int? primaryColor;
|
||||
@override
|
||||
@JsonKey()
|
||||
@@ -1708,12 +1786,14 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
other.fontFamily == fontFamily));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, primaryColor, themeMode, prueBlack, fontFamily);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ThemePropsImplCopyWith<_$ThemePropsImpl> get copyWith =>
|
||||
@@ -1745,8 +1825,11 @@ abstract class _ThemeProps implements ThemeProps {
|
||||
bool get prueBlack;
|
||||
@override
|
||||
FontFamily get fontFamily;
|
||||
|
||||
/// Create a copy of ThemeProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ThemePropsImplCopyWith<_$ThemePropsImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
|
||||
..windowProps =
|
||||
WindowProps.fromJson(json['windowProps'] as Map<String, dynamic>?)
|
||||
..vpnProps = VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?)
|
||||
..desktopProps =
|
||||
DesktopProps.fromJson(json['desktopProps'] as Map<String, dynamic>?)
|
||||
..networkProps =
|
||||
NetworkProps.fromJson(json['networkProps'] as Map<String, dynamic>?)
|
||||
..overrideDns = json['overrideDns'] as bool? ?? false
|
||||
..hotKeyActions = (json['hotKeyActions'] as List<dynamic>?)
|
||||
?.map((e) => HotKeyAction.fromJson(e as Map<String, dynamic>))
|
||||
@@ -44,7 +44,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
|
||||
'dav': instance.dav,
|
||||
'windowProps': instance.windowProps,
|
||||
'vpnProps': instance.vpnProps,
|
||||
'desktopProps': instance.desktopProps,
|
||||
'networkProps': instance.networkProps,
|
||||
'overrideDns': instance.overrideDns,
|
||||
'hotKeyActions': instance.hotKeyActions,
|
||||
'proxiesStyle': instance.proxiesStyle,
|
||||
@@ -128,7 +128,7 @@ const _$AccessSortTypeEnumMap = {
|
||||
|
||||
_$WindowPropsImpl _$$WindowPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$WindowPropsImpl(
|
||||
width: (json['width'] as num?)?.toDouble() ?? 1000,
|
||||
width: (json['width'] as num?)?.toDouble() ?? 900,
|
||||
height: (json['height'] as num?)?.toDouble() ?? 600,
|
||||
top: (json['top'] as num?)?.toDouble(),
|
||||
left: (json['left'] as num?)?.toDouble(),
|
||||
@@ -148,10 +148,6 @@ _$VpnPropsImpl _$$VpnPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
systemProxy: json['systemProxy'] as bool? ?? true,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
allowBypass: json['allowBypass'] as bool? ?? true,
|
||||
bypassDomain: (json['bypassDomain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
defaultBypassDomain,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$VpnPropsImplToJson(_$VpnPropsImpl instance) =>
|
||||
@@ -160,17 +156,21 @@ Map<String, dynamic> _$$VpnPropsImplToJson(_$VpnPropsImpl instance) =>
|
||||
'systemProxy': instance.systemProxy,
|
||||
'ipv6': instance.ipv6,
|
||||
'allowBypass': instance.allowBypass,
|
||||
'bypassDomain': instance.bypassDomain,
|
||||
};
|
||||
|
||||
_$DesktopPropsImpl _$$DesktopPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$DesktopPropsImpl(
|
||||
_$NetworkPropsImpl _$$NetworkPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$NetworkPropsImpl(
|
||||
systemProxy: json['systemProxy'] as bool? ?? true,
|
||||
bypassDomain: (json['bypassDomain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
defaultBypassDomain,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DesktopPropsImplToJson(_$DesktopPropsImpl instance) =>
|
||||
Map<String, dynamic> _$$NetworkPropsImplToJson(_$NetworkPropsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'systemProxy': instance.systemProxy,
|
||||
'bypassDomain': instance.bypassDomain,
|
||||
};
|
||||
|
||||
_$ProxiesStyleImpl _$$ProxiesStyleImplFromJson(Map<String, dynamic> json) =>
|
||||
@@ -234,7 +234,7 @@ const _$ProxyCardTypeEnumMap = {
|
||||
|
||||
_$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ThemePropsImpl(
|
||||
primaryColor: (json['primaryColor'] as num?)?.toInt() ?? 0xFF795548,
|
||||
primaryColor: (json['primaryColor'] as num?)?.toInt(),
|
||||
themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
|
||||
ThemeMode.system,
|
||||
prueBlack: json['prueBlack'] as bool? ?? false,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,9 @@ _$CoreStateImpl _$$CoreStateImplFromJson(Map<String, dynamic> json) =>
|
||||
bypassDomain: (json['bypassDomain'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
routeAddress: (json['routeAddress'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
ipv6: json['ipv6'] as bool,
|
||||
onlyProxy: json['onlyProxy'] as bool,
|
||||
);
|
||||
@@ -31,6 +34,7 @@ Map<String, dynamic> _$$CoreStateImplToJson(_$CoreStateImpl instance) =>
|
||||
'allowBypass': instance.allowBypass,
|
||||
'systemProxy': instance.systemProxy,
|
||||
'bypassDomain': instance.bypassDomain,
|
||||
'routeAddress': instance.routeAddress,
|
||||
'ipv6': instance.ipv6,
|
||||
'onlyProxy': instance.onlyProxy,
|
||||
};
|
||||
@@ -51,6 +55,9 @@ _$AndroidVpnOptionsImpl _$$AndroidVpnOptionsImplFromJson(
|
||||
.toList(),
|
||||
ipv4Address: json['ipv4Address'] as String,
|
||||
ipv6Address: json['ipv6Address'] as String,
|
||||
routeAddress: (json['routeAddress'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
dnsServerAddress: json['dnsServerAddress'] as String,
|
||||
);
|
||||
|
||||
@@ -65,6 +72,7 @@ Map<String, dynamic> _$$AndroidVpnOptionsImplToJson(
|
||||
'bypassDomain': instance.bypassDomain,
|
||||
'ipv4Address': instance.ipv4Address,
|
||||
'ipv6Address': instance.ipv6Address,
|
||||
'routeAddress': instance.routeAddress,
|
||||
'dnsServerAddress': instance.dnsServerAddress,
|
||||
};
|
||||
|
||||
@@ -215,13 +223,33 @@ Map<String, dynamic> _$$ProcessMapItemImplToJson(
|
||||
'value': instance.value,
|
||||
};
|
||||
|
||||
_$ProviderSubscriptionInfoImpl _$$ProviderSubscriptionInfoImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ProviderSubscriptionInfoImpl(
|
||||
upload: (json['UPLOAD'] as num?)?.toInt() ?? 0,
|
||||
download: (json['DOWNLOAD'] as num?)?.toInt() ?? 0,
|
||||
total: (json['TOTAL'] as num?)?.toInt() ?? 0,
|
||||
expire: (json['EXPIRE'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProviderSubscriptionInfoImplToJson(
|
||||
_$ProviderSubscriptionInfoImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'UPLOAD': instance.upload,
|
||||
'DOWNLOAD': instance.download,
|
||||
'TOTAL': instance.total,
|
||||
'EXPIRE': instance.expire,
|
||||
};
|
||||
|
||||
_$ExternalProviderImpl _$$ExternalProviderImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ExternalProviderImpl(
|
||||
name: json['name'] as String,
|
||||
type: json['type'] as String,
|
||||
path: json['path'] as String,
|
||||
path: json['path'] as String?,
|
||||
count: (json['count'] as num).toInt(),
|
||||
subscriptionInfo: subscriptionInfoFormCore(
|
||||
json['subscription-info'] as Map<String, Object?>?),
|
||||
isUpdating: json['isUpdating'] as bool? ?? false,
|
||||
vehicleType: json['vehicle-type'] as String,
|
||||
updateAt: DateTime.parse(json['update-at'] as String),
|
||||
@@ -234,6 +262,7 @@ Map<String, dynamic> _$$ExternalProviderImplToJson(
|
||||
'type': instance.type,
|
||||
'path': instance.path,
|
||||
'count': instance.count,
|
||||
'subscription-info': instance.subscriptionInfo,
|
||||
'isUpdating': instance.isUpdating,
|
||||
'vehicle-type': instance.vehicleType,
|
||||
'update-at': instance.updateAt.toIso8601String(),
|
||||
|
||||
@@ -14,41 +14,48 @@ T _$identity<T>(T value) => value;
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
UserInfo _$UserInfoFromJson(Map<String, dynamic> json) {
|
||||
return _UserInfo.fromJson(json);
|
||||
SubscriptionInfo _$SubscriptionInfoFromJson(Map<String, dynamic> json) {
|
||||
return _SubscriptionInfo.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$UserInfo {
|
||||
mixin _$SubscriptionInfo {
|
||||
int get upload => throw _privateConstructorUsedError;
|
||||
int get download => throw _privateConstructorUsedError;
|
||||
int get total => throw _privateConstructorUsedError;
|
||||
int get expire => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this SubscriptionInfo to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$UserInfoCopyWith<UserInfo> get copyWith =>
|
||||
|
||||
/// Create a copy of SubscriptionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$SubscriptionInfoCopyWith<SubscriptionInfo> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $UserInfoCopyWith<$Res> {
|
||||
factory $UserInfoCopyWith(UserInfo value, $Res Function(UserInfo) then) =
|
||||
_$UserInfoCopyWithImpl<$Res, UserInfo>;
|
||||
abstract class $SubscriptionInfoCopyWith<$Res> {
|
||||
factory $SubscriptionInfoCopyWith(
|
||||
SubscriptionInfo value, $Res Function(SubscriptionInfo) then) =
|
||||
_$SubscriptionInfoCopyWithImpl<$Res, SubscriptionInfo>;
|
||||
@useResult
|
||||
$Res call({int upload, int download, int total, int expire});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$UserInfoCopyWithImpl<$Res, $Val extends UserInfo>
|
||||
implements $UserInfoCopyWith<$Res> {
|
||||
_$UserInfoCopyWithImpl(this._value, this._then);
|
||||
class _$SubscriptionInfoCopyWithImpl<$Res, $Val extends SubscriptionInfo>
|
||||
implements $SubscriptionInfoCopyWith<$Res> {
|
||||
_$SubscriptionInfoCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of SubscriptionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -79,24 +86,26 @@ class _$UserInfoCopyWithImpl<$Res, $Val extends UserInfo>
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$UserInfoImplCopyWith<$Res>
|
||||
implements $UserInfoCopyWith<$Res> {
|
||||
factory _$$UserInfoImplCopyWith(
|
||||
_$UserInfoImpl value, $Res Function(_$UserInfoImpl) then) =
|
||||
__$$UserInfoImplCopyWithImpl<$Res>;
|
||||
abstract class _$$SubscriptionInfoImplCopyWith<$Res>
|
||||
implements $SubscriptionInfoCopyWith<$Res> {
|
||||
factory _$$SubscriptionInfoImplCopyWith(_$SubscriptionInfoImpl value,
|
||||
$Res Function(_$SubscriptionInfoImpl) then) =
|
||||
__$$SubscriptionInfoImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({int upload, int download, int total, int expire});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$UserInfoImplCopyWithImpl<$Res>
|
||||
extends _$UserInfoCopyWithImpl<$Res, _$UserInfoImpl>
|
||||
implements _$$UserInfoImplCopyWith<$Res> {
|
||||
__$$UserInfoImplCopyWithImpl(
|
||||
_$UserInfoImpl _value, $Res Function(_$UserInfoImpl) _then)
|
||||
class __$$SubscriptionInfoImplCopyWithImpl<$Res>
|
||||
extends _$SubscriptionInfoCopyWithImpl<$Res, _$SubscriptionInfoImpl>
|
||||
implements _$$SubscriptionInfoImplCopyWith<$Res> {
|
||||
__$$SubscriptionInfoImplCopyWithImpl(_$SubscriptionInfoImpl _value,
|
||||
$Res Function(_$SubscriptionInfoImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of SubscriptionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -105,7 +114,7 @@ class __$$UserInfoImplCopyWithImpl<$Res>
|
||||
Object? total = null,
|
||||
Object? expire = null,
|
||||
}) {
|
||||
return _then(_$UserInfoImpl(
|
||||
return _then(_$SubscriptionInfoImpl(
|
||||
upload: null == upload
|
||||
? _value.upload
|
||||
: upload // ignore: cast_nullable_to_non_nullable
|
||||
@@ -128,12 +137,12 @@ class __$$UserInfoImplCopyWithImpl<$Res>
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$UserInfoImpl implements _UserInfo {
|
||||
const _$UserInfoImpl(
|
||||
class _$SubscriptionInfoImpl implements _SubscriptionInfo {
|
||||
const _$SubscriptionInfoImpl(
|
||||
{this.upload = 0, this.download = 0, this.total = 0, this.expire = 0});
|
||||
|
||||
factory _$UserInfoImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$UserInfoImplFromJson(json);
|
||||
factory _$SubscriptionInfoImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$SubscriptionInfoImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
@@ -150,14 +159,14 @@ class _$UserInfoImpl implements _UserInfo {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserInfo(upload: $upload, download: $download, total: $total, expire: $expire)';
|
||||
return 'SubscriptionInfo(upload: $upload, download: $download, total: $total, expire: $expire)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$UserInfoImpl &&
|
||||
other is _$SubscriptionInfoImpl &&
|
||||
(identical(other.upload, upload) || other.upload == upload) &&
|
||||
(identical(other.download, download) ||
|
||||
other.download == download) &&
|
||||
@@ -165,33 +174,36 @@ class _$UserInfoImpl implements _UserInfo {
|
||||
(identical(other.expire, expire) || other.expire == expire));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, upload, download, total, expire);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of SubscriptionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$UserInfoImplCopyWith<_$UserInfoImpl> get copyWith =>
|
||||
__$$UserInfoImplCopyWithImpl<_$UserInfoImpl>(this, _$identity);
|
||||
_$$SubscriptionInfoImplCopyWith<_$SubscriptionInfoImpl> get copyWith =>
|
||||
__$$SubscriptionInfoImplCopyWithImpl<_$SubscriptionInfoImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$UserInfoImplToJson(
|
||||
return _$$SubscriptionInfoImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _UserInfo implements UserInfo {
|
||||
const factory _UserInfo(
|
||||
abstract class _SubscriptionInfo implements SubscriptionInfo {
|
||||
const factory _SubscriptionInfo(
|
||||
{final int upload,
|
||||
final int download,
|
||||
final int total,
|
||||
final int expire}) = _$UserInfoImpl;
|
||||
final int expire}) = _$SubscriptionInfoImpl;
|
||||
|
||||
factory _UserInfo.fromJson(Map<String, dynamic> json) =
|
||||
_$UserInfoImpl.fromJson;
|
||||
factory _SubscriptionInfo.fromJson(Map<String, dynamic> json) =
|
||||
_$SubscriptionInfoImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get upload;
|
||||
@@ -201,9 +213,12 @@ abstract class _UserInfo implements UserInfo {
|
||||
int get total;
|
||||
@override
|
||||
int get expire;
|
||||
|
||||
/// Create a copy of SubscriptionInfo
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$UserInfoImplCopyWith<_$UserInfoImpl> get copyWith =>
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$SubscriptionInfoImplCopyWith<_$SubscriptionInfoImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -219,15 +234,19 @@ mixin _$Profile {
|
||||
String get url => throw _privateConstructorUsedError;
|
||||
DateTime? get lastUpdateDate => throw _privateConstructorUsedError;
|
||||
Duration get autoUpdateDuration => throw _privateConstructorUsedError;
|
||||
UserInfo? get userInfo => throw _privateConstructorUsedError;
|
||||
SubscriptionInfo? get subscriptionInfo => throw _privateConstructorUsedError;
|
||||
bool get autoUpdate => throw _privateConstructorUsedError;
|
||||
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
|
||||
Set<String> get unfoldSet => throw _privateConstructorUsedError;
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
bool get isUpdating => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Profile to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
/// Create a copy of Profile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ProfileCopyWith<Profile> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -243,13 +262,13 @@ abstract class $ProfileCopyWith<$Res> {
|
||||
String url,
|
||||
DateTime? lastUpdateDate,
|
||||
Duration autoUpdateDuration,
|
||||
UserInfo? userInfo,
|
||||
SubscriptionInfo? subscriptionInfo,
|
||||
bool autoUpdate,
|
||||
Map<String, String> selectedMap,
|
||||
Set<String> unfoldSet,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false) bool isUpdating});
|
||||
|
||||
$UserInfoCopyWith<$Res>? get userInfo;
|
||||
$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -262,6 +281,8 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Profile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -271,7 +292,7 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
|
||||
Object? url = null,
|
||||
Object? lastUpdateDate = freezed,
|
||||
Object? autoUpdateDuration = null,
|
||||
Object? userInfo = freezed,
|
||||
Object? subscriptionInfo = freezed,
|
||||
Object? autoUpdate = null,
|
||||
Object? selectedMap = null,
|
||||
Object? unfoldSet = null,
|
||||
@@ -302,10 +323,10 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
|
||||
? _value.autoUpdateDuration
|
||||
: autoUpdateDuration // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
userInfo: freezed == userInfo
|
||||
? _value.userInfo
|
||||
: userInfo // ignore: cast_nullable_to_non_nullable
|
||||
as UserInfo?,
|
||||
subscriptionInfo: freezed == subscriptionInfo
|
||||
? _value.subscriptionInfo
|
||||
: subscriptionInfo // ignore: cast_nullable_to_non_nullable
|
||||
as SubscriptionInfo?,
|
||||
autoUpdate: null == autoUpdate
|
||||
? _value.autoUpdate
|
||||
: autoUpdate // ignore: cast_nullable_to_non_nullable
|
||||
@@ -325,15 +346,17 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of Profile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$UserInfoCopyWith<$Res>? get userInfo {
|
||||
if (_value.userInfo == null) {
|
||||
$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo {
|
||||
if (_value.subscriptionInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $UserInfoCopyWith<$Res>(_value.userInfo!, (value) {
|
||||
return _then(_value.copyWith(userInfo: value) as $Val);
|
||||
return $SubscriptionInfoCopyWith<$Res>(_value.subscriptionInfo!, (value) {
|
||||
return _then(_value.copyWith(subscriptionInfo: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -352,14 +375,14 @@ abstract class _$$ProfileImplCopyWith<$Res> implements $ProfileCopyWith<$Res> {
|
||||
String url,
|
||||
DateTime? lastUpdateDate,
|
||||
Duration autoUpdateDuration,
|
||||
UserInfo? userInfo,
|
||||
SubscriptionInfo? subscriptionInfo,
|
||||
bool autoUpdate,
|
||||
Map<String, String> selectedMap,
|
||||
Set<String> unfoldSet,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false) bool isUpdating});
|
||||
|
||||
@override
|
||||
$UserInfoCopyWith<$Res>? get userInfo;
|
||||
$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -370,6 +393,8 @@ class __$$ProfileImplCopyWithImpl<$Res>
|
||||
_$ProfileImpl _value, $Res Function(_$ProfileImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Profile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
@@ -379,7 +404,7 @@ class __$$ProfileImplCopyWithImpl<$Res>
|
||||
Object? url = null,
|
||||
Object? lastUpdateDate = freezed,
|
||||
Object? autoUpdateDuration = null,
|
||||
Object? userInfo = freezed,
|
||||
Object? subscriptionInfo = freezed,
|
||||
Object? autoUpdate = null,
|
||||
Object? selectedMap = null,
|
||||
Object? unfoldSet = null,
|
||||
@@ -410,10 +435,10 @@ class __$$ProfileImplCopyWithImpl<$Res>
|
||||
? _value.autoUpdateDuration
|
||||
: autoUpdateDuration // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
userInfo: freezed == userInfo
|
||||
? _value.userInfo
|
||||
: userInfo // ignore: cast_nullable_to_non_nullable
|
||||
as UserInfo?,
|
||||
subscriptionInfo: freezed == subscriptionInfo
|
||||
? _value.subscriptionInfo
|
||||
: subscriptionInfo // ignore: cast_nullable_to_non_nullable
|
||||
as SubscriptionInfo?,
|
||||
autoUpdate: null == autoUpdate
|
||||
? _value.autoUpdate
|
||||
: autoUpdate // ignore: cast_nullable_to_non_nullable
|
||||
@@ -444,7 +469,7 @@ class _$ProfileImpl implements _Profile {
|
||||
this.url = "",
|
||||
this.lastUpdateDate,
|
||||
required this.autoUpdateDuration,
|
||||
this.userInfo,
|
||||
this.subscriptionInfo,
|
||||
this.autoUpdate = true,
|
||||
final Map<String, String> selectedMap = const {},
|
||||
final Set<String> unfoldSet = const {},
|
||||
@@ -470,7 +495,7 @@ class _$ProfileImpl implements _Profile {
|
||||
@override
|
||||
final Duration autoUpdateDuration;
|
||||
@override
|
||||
final UserInfo? userInfo;
|
||||
final SubscriptionInfo? subscriptionInfo;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool autoUpdate;
|
||||
@@ -498,7 +523,7 @@ class _$ProfileImpl implements _Profile {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Profile(id: $id, label: $label, currentGroupName: $currentGroupName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, userInfo: $userInfo, autoUpdate: $autoUpdate, selectedMap: $selectedMap, unfoldSet: $unfoldSet, isUpdating: $isUpdating)';
|
||||
return 'Profile(id: $id, label: $label, currentGroupName: $currentGroupName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, subscriptionInfo: $subscriptionInfo, autoUpdate: $autoUpdate, selectedMap: $selectedMap, unfoldSet: $unfoldSet, isUpdating: $isUpdating)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -515,8 +540,8 @@ class _$ProfileImpl implements _Profile {
|
||||
other.lastUpdateDate == lastUpdateDate) &&
|
||||
(identical(other.autoUpdateDuration, autoUpdateDuration) ||
|
||||
other.autoUpdateDuration == autoUpdateDuration) &&
|
||||
(identical(other.userInfo, userInfo) ||
|
||||
other.userInfo == userInfo) &&
|
||||
(identical(other.subscriptionInfo, subscriptionInfo) ||
|
||||
other.subscriptionInfo == subscriptionInfo) &&
|
||||
(identical(other.autoUpdate, autoUpdate) ||
|
||||
other.autoUpdate == autoUpdate) &&
|
||||
const DeepCollectionEquality()
|
||||
@@ -527,7 +552,7 @@ class _$ProfileImpl implements _Profile {
|
||||
other.isUpdating == isUpdating));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
@@ -537,13 +562,15 @@ class _$ProfileImpl implements _Profile {
|
||||
url,
|
||||
lastUpdateDate,
|
||||
autoUpdateDuration,
|
||||
userInfo,
|
||||
subscriptionInfo,
|
||||
autoUpdate,
|
||||
const DeepCollectionEquality().hash(_selectedMap),
|
||||
const DeepCollectionEquality().hash(_unfoldSet),
|
||||
isUpdating);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
/// Create a copy of Profile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProfileImplCopyWith<_$ProfileImpl> get copyWith =>
|
||||
@@ -565,7 +592,7 @@ abstract class _Profile implements Profile {
|
||||
final String url,
|
||||
final DateTime? lastUpdateDate,
|
||||
required final Duration autoUpdateDuration,
|
||||
final UserInfo? userInfo,
|
||||
final SubscriptionInfo? subscriptionInfo,
|
||||
final bool autoUpdate,
|
||||
final Map<String, String> selectedMap,
|
||||
final Set<String> unfoldSet,
|
||||
@@ -587,7 +614,7 @@ abstract class _Profile implements Profile {
|
||||
@override
|
||||
Duration get autoUpdateDuration;
|
||||
@override
|
||||
UserInfo? get userInfo;
|
||||
SubscriptionInfo? get subscriptionInfo;
|
||||
@override
|
||||
bool get autoUpdate;
|
||||
@override
|
||||
@@ -597,8 +624,11 @@ abstract class _Profile implements Profile {
|
||||
@override
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
bool get isUpdating;
|
||||
|
||||
/// Create a copy of Profile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ProfileImplCopyWith<_$ProfileImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@@ -6,15 +6,17 @@ part of '../profile.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$UserInfoImpl _$$UserInfoImplFromJson(Map<String, dynamic> json) =>
|
||||
_$UserInfoImpl(
|
||||
_$SubscriptionInfoImpl _$$SubscriptionInfoImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SubscriptionInfoImpl(
|
||||
upload: (json['upload'] as num?)?.toInt() ?? 0,
|
||||
download: (json['download'] as num?)?.toInt() ?? 0,
|
||||
total: (json['total'] as num?)?.toInt() ?? 0,
|
||||
expire: (json['expire'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UserInfoImplToJson(_$UserInfoImpl instance) =>
|
||||
Map<String, dynamic> _$$SubscriptionInfoImplToJson(
|
||||
_$SubscriptionInfoImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'upload': instance.upload,
|
||||
'download': instance.download,
|
||||
@@ -33,9 +35,10 @@ _$ProfileImpl _$$ProfileImplFromJson(Map<String, dynamic> json) =>
|
||||
: DateTime.parse(json['lastUpdateDate'] as String),
|
||||
autoUpdateDuration:
|
||||
Duration(microseconds: (json['autoUpdateDuration'] as num).toInt()),
|
||||
userInfo: json['userInfo'] == null
|
||||
subscriptionInfo: json['subscriptionInfo'] == null
|
||||
? null
|
||||
: UserInfo.fromJson(json['userInfo'] as Map<String, dynamic>),
|
||||
: SubscriptionInfo.fromJson(
|
||||
json['subscriptionInfo'] as Map<String, dynamic>),
|
||||
autoUpdate: json['autoUpdate'] as bool? ?? true,
|
||||
selectedMap: (json['selectedMap'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
@@ -55,7 +58,7 @@ Map<String, dynamic> _$$ProfileImplToJson(_$ProfileImpl instance) =>
|
||||
'url': instance.url,
|
||||
'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(),
|
||||
'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds,
|
||||
'userInfo': instance.userInfo,
|
||||
'subscriptionInfo': instance.subscriptionInfo,
|
||||
'autoUpdate': instance.autoUpdate,
|
||||
'selectedMap': instance.selectedMap,
|
||||
'unfoldSet': instance.unfoldSet.toList(),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,37 +4,36 @@ import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:fl_clash/clash/core.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'generated/profile.g.dart';
|
||||
|
||||
part 'generated/profile.freezed.dart';
|
||||
part 'generated/profile.g.dart';
|
||||
|
||||
typedef SelectedMap = Map<String, String>;
|
||||
|
||||
@freezed
|
||||
class UserInfo with _$UserInfo {
|
||||
const factory UserInfo({
|
||||
class SubscriptionInfo with _$SubscriptionInfo {
|
||||
const factory SubscriptionInfo({
|
||||
@Default(0) int upload,
|
||||
@Default(0) int download,
|
||||
@Default(0) int total,
|
||||
@Default(0) int expire,
|
||||
}) = _UserInfo;
|
||||
}) = _SubscriptionInfo;
|
||||
|
||||
factory UserInfo.fromJson(Map<String, Object?> json) =>
|
||||
_$UserInfoFromJson(json);
|
||||
factory SubscriptionInfo.fromJson(Map<String, Object?> json) =>
|
||||
_$SubscriptionInfoFromJson(json);
|
||||
|
||||
factory UserInfo.formHString(String? info) {
|
||||
if (info == null) return const UserInfo();
|
||||
factory SubscriptionInfo.formHString(String? info) {
|
||||
if (info == null) return const SubscriptionInfo();
|
||||
final list = info.split(";");
|
||||
Map<String, int?> map = {};
|
||||
for (final i in list) {
|
||||
final keyValue = i.trim().split("=");
|
||||
map[keyValue[0]] = int.tryParse(keyValue[1]);
|
||||
}
|
||||
return UserInfo(
|
||||
return SubscriptionInfo(
|
||||
upload: map["upload"] ?? 0,
|
||||
download: map["download"] ?? 0,
|
||||
total: map["total"] ?? 0,
|
||||
@@ -52,7 +51,7 @@ class Profile with _$Profile {
|
||||
@Default("") String url,
|
||||
DateTime? lastUpdateDate,
|
||||
required Duration autoUpdateDuration,
|
||||
UserInfo? userInfo,
|
||||
SubscriptionInfo? subscriptionInfo,
|
||||
@Default(true) bool autoUpdate,
|
||||
@Default({}) SelectedMap selectedMap,
|
||||
@Default({}) Set<String> unfoldSet,
|
||||
@@ -103,7 +102,7 @@ extension ProfileExtension on Profile {
|
||||
final userinfo = response.headers.value('subscription-userinfo');
|
||||
return await copyWith(
|
||||
label: label ?? other.getFileNameForDisposition(disposition) ?? id,
|
||||
userInfo: UserInfo.formHString(userinfo),
|
||||
subscriptionInfo: SubscriptionInfo.formHString(userinfo),
|
||||
).saveFile(response.data);
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ class ProxyState with _$ProxyState {
|
||||
const factory ProxyState({
|
||||
required bool isStart,
|
||||
required bool systemProxy,
|
||||
required List<String> bassDomain,
|
||||
required int port,
|
||||
}) = _ProxyState;
|
||||
}
|
||||
@@ -218,8 +219,6 @@ class HttpOverridesState with _$HttpOverridesState {
|
||||
}) = _HttpOverridesState;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@freezed
|
||||
class ClashConfigState with _$ClashConfigState {
|
||||
const factory ClashConfigState({
|
||||
@@ -251,4 +250,4 @@ class VPNState with _$VPNState {
|
||||
required TunStack stack,
|
||||
required VpnProps vpnProps,
|
||||
}) = _VPNState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/common/app_localizations.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class App {
|
||||
static App? _instance;
|
||||
@@ -20,6 +22,12 @@ class App {
|
||||
if (onExit != null) {
|
||||
await onExit!();
|
||||
}
|
||||
case "getText":
|
||||
try {
|
||||
return Intl.message(call.arguments as String);
|
||||
} catch (_) {
|
||||
return "";
|
||||
}
|
||||
default:
|
||||
throw MissingPluginException();
|
||||
}
|
||||
@@ -78,6 +86,13 @@ class App {
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool?> initShortcuts() async {
|
||||
return await methodChannel.invokeMethod<bool>(
|
||||
"initShortcuts",
|
||||
appLocalizations.toggle,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool?> updateExcludeFromRecents(bool value) async {
|
||||
return await methodChannel.invokeMethod<bool>("updateExcludeFromRecents", {
|
||||
"value": value,
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FadePage<T> extends Page<T> {
|
||||
final Widget child;
|
||||
final bool maintainState;
|
||||
final bool fullscreenDialog;
|
||||
final bool allowSnapshotting;
|
||||
|
||||
const FadePage({
|
||||
required this.child,
|
||||
this.maintainState = true,
|
||||
this.fullscreenDialog = false,
|
||||
this.allowSnapshotting = true,
|
||||
super.key,
|
||||
super.name,
|
||||
super.arguments,
|
||||
super.restorationId,
|
||||
});
|
||||
|
||||
@override
|
||||
Route<T> createRoute(BuildContext context) {
|
||||
return FadePageRoute<T>(page: this);
|
||||
}
|
||||
}
|
||||
|
||||
class FadePageRoute<T> extends PageRoute<T> {
|
||||
final FadePage page;
|
||||
|
||||
FadePageRoute({
|
||||
required this.page,
|
||||
}) : super(settings: page);
|
||||
|
||||
FadePage<T> get _page => settings as FadePage<T>;
|
||||
|
||||
@override
|
||||
Widget buildPage(BuildContext context, Animation<double> animation,
|
||||
Animation<double> secondaryAnimation) {
|
||||
return _page.child;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildTransitions(BuildContext context, Animation<double> animation,
|
||||
Animation<double> secondaryAnimation, Widget child) {
|
||||
return FadeTransition(
|
||||
opacity: animation,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => const Duration(milliseconds: 600);
|
||||
|
||||
@override
|
||||
bool get maintainState => false;
|
||||
|
||||
@override
|
||||
Color? get barrierColor => null;
|
||||
|
||||
@override
|
||||
String? get barrierLabel => null;
|
||||
}
|
||||
168
lib/state.dart
168
lib/state.dart
@@ -3,16 +3,19 @@ import 'dart:io';
|
||||
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/plugins/service.dart';
|
||||
import 'package:fl_clash/plugins/vpn.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'common/common.dart';
|
||||
import 'controller.dart';
|
||||
import 'models/models.dart';
|
||||
import 'common/common.dart';
|
||||
|
||||
class GlobalState {
|
||||
Timer? timer;
|
||||
@@ -70,10 +73,7 @@ class GlobalState {
|
||||
appState.versionInfo = clashCore.getVersionInfo();
|
||||
}
|
||||
|
||||
handleStart({
|
||||
required Config config,
|
||||
required ClashConfig clashConfig,
|
||||
}) async {
|
||||
handleStart() async {
|
||||
clashCore.start();
|
||||
if (globalState.isVpnService) {
|
||||
await vpn?.startVpn();
|
||||
@@ -81,8 +81,6 @@ class GlobalState {
|
||||
return;
|
||||
}
|
||||
startTime ??= DateTime.now();
|
||||
await preferences.saveClashConfig(clashConfig);
|
||||
await preferences.saveConfig(config);
|
||||
await service?.init();
|
||||
startListenUpdate();
|
||||
}
|
||||
@@ -106,6 +104,7 @@ class GlobalState {
|
||||
required Config config,
|
||||
required ClashConfig clashConfig,
|
||||
}) async {
|
||||
clashCore.requestGc();
|
||||
await updateClashConfig(
|
||||
clashConfig: clashConfig,
|
||||
config: config,
|
||||
@@ -130,20 +129,23 @@ class GlobalState {
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
clashCore.setState(
|
||||
CoreState(
|
||||
enable: config.vpnProps.enable,
|
||||
accessControl: config.isAccessControl ? config.accessControl : null,
|
||||
ipv6: config.vpnProps.ipv6,
|
||||
allowBypass: config.vpnProps.allowBypass,
|
||||
systemProxy: config.vpnProps.systemProxy,
|
||||
onlyProxy: config.appSetting.onlyProxy,
|
||||
bypassDomain: config.networkProps.bypassDomain,
|
||||
routeAddress: clashConfig.routeAddress,
|
||||
currentProfileName:
|
||||
config.currentProfile?.label ?? config.currentProfileId ?? "",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
clashCore.setState(
|
||||
CoreState(
|
||||
enable: config.vpnProps.enable,
|
||||
accessControl: config.isAccessControl ? config.accessControl : null,
|
||||
ipv6: config.vpnProps.ipv6,
|
||||
allowBypass: config.vpnProps.allowBypass,
|
||||
systemProxy: config.vpnProps.systemProxy,
|
||||
onlyProxy: config.appSetting.onlyProxy,
|
||||
bypassDomain: config.vpnProps.bypassDomain,
|
||||
currentProfileName:
|
||||
config.currentProfile?.label ?? config.currentProfileId ?? "",
|
||||
),
|
||||
);
|
||||
updateCoreVersionInfo(appState);
|
||||
}
|
||||
|
||||
@@ -299,6 +301,132 @@ class GlobalState {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future _updateSystemTray({
|
||||
required Brightness? brightness,
|
||||
bool force = false,
|
||||
}) async {
|
||||
if (Platform.isAndroid) {
|
||||
return;
|
||||
}
|
||||
if (Platform.isLinux || force) {
|
||||
await trayManager.destroy();
|
||||
}
|
||||
await trayManager.setIcon(
|
||||
other.getTrayIconPath(
|
||||
brightness: brightness ??
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness,
|
||||
),
|
||||
isTemplate: true,
|
||||
);
|
||||
if (!Platform.isLinux) {
|
||||
await trayManager.setToolTip(
|
||||
appName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
updateTray({
|
||||
required AppState appState,
|
||||
required AppFlowingState appFlowingState,
|
||||
required Config config,
|
||||
required ClashConfig clashConfig,
|
||||
bool focus = false,
|
||||
}) async {
|
||||
if (!Platform.isLinux) {
|
||||
await _updateSystemTray(
|
||||
brightness: appState.brightness,
|
||||
force: focus,
|
||||
);
|
||||
}
|
||||
List<MenuItem> menuItems = [];
|
||||
final showMenuItem = MenuItem(
|
||||
label: appLocalizations.show,
|
||||
onClick: (_) {
|
||||
window?.show();
|
||||
},
|
||||
);
|
||||
menuItems.add(showMenuItem);
|
||||
final startMenuItem = MenuItem.checkbox(
|
||||
label: appFlowingState.isStart
|
||||
? appLocalizations.stop
|
||||
: appLocalizations.start,
|
||||
onClick: (_) async {
|
||||
globalState.appController.updateStart();
|
||||
},
|
||||
checked: false,
|
||||
);
|
||||
menuItems.add(startMenuItem);
|
||||
menuItems.add(MenuItem.separator());
|
||||
for (final mode in Mode.values) {
|
||||
menuItems.add(
|
||||
MenuItem.checkbox(
|
||||
label: Intl.message(mode.name),
|
||||
onClick: (_) {
|
||||
globalState.appController.clashConfig.mode = mode;
|
||||
},
|
||||
checked: mode == clashConfig.mode,
|
||||
),
|
||||
);
|
||||
}
|
||||
menuItems.add(MenuItem.separator());
|
||||
if (appFlowingState.isStart) {
|
||||
menuItems.add(
|
||||
MenuItem.checkbox(
|
||||
label: appLocalizations.tun,
|
||||
onClick: (_) {
|
||||
globalState.appController.updateTun();
|
||||
},
|
||||
checked: clashConfig.tun.enable,
|
||||
),
|
||||
);
|
||||
menuItems.add(
|
||||
MenuItem.checkbox(
|
||||
label: appLocalizations.systemProxy,
|
||||
onClick: (_) {
|
||||
globalState.appController.updateSystemProxy();
|
||||
},
|
||||
checked: config.networkProps.systemProxy,
|
||||
),
|
||||
);
|
||||
menuItems.add(MenuItem.separator());
|
||||
}
|
||||
final autoStartMenuItem = MenuItem.checkbox(
|
||||
label: appLocalizations.autoLaunch,
|
||||
onClick: (_) async {
|
||||
globalState.appController.updateAutoLaunch();
|
||||
},
|
||||
checked: config.appSetting.autoLaunch,
|
||||
);
|
||||
menuItems.add(autoStartMenuItem);
|
||||
|
||||
if (Platform.isWindows) {
|
||||
final adminAutoStartMenuItem = MenuItem.checkbox(
|
||||
label: appLocalizations.adminAutoLaunch,
|
||||
onClick: (_) async {
|
||||
globalState.appController.updateAdminAutoLaunch();
|
||||
},
|
||||
checked: config.appSetting.adminAutoLaunch,
|
||||
);
|
||||
menuItems.add(adminAutoStartMenuItem);
|
||||
}
|
||||
menuItems.add(MenuItem.separator());
|
||||
final exitMenuItem = MenuItem(
|
||||
label: appLocalizations.exit,
|
||||
onClick: (_) async {
|
||||
await globalState.appController.handleExit();
|
||||
},
|
||||
);
|
||||
menuItems.add(exitMenuItem);
|
||||
final menu = Menu(items: menuItems);
|
||||
await trayManager.setContextMenu(menu);
|
||||
if (Platform.isLinux) {
|
||||
await _updateSystemTray(
|
||||
brightness: appState.brightness,
|
||||
force: focus,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final globalState = GlobalState();
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import 'dart:io';
|
||||
|
||||
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
@@ -19,7 +17,7 @@ class _PopContainerState extends State<BackScope> {
|
||||
if (Platform.isAndroid) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (_) async {
|
||||
onPopInvokedWithResult: (_, __) async {
|
||||
final canPop = Navigator.canPop(context);
|
||||
if (canPop) {
|
||||
Navigator.pop(context);
|
||||
|
||||
@@ -68,6 +68,8 @@ class ProxiesActionsBuilder extends StatelessWidget {
|
||||
|
||||
typedef StateWidgetBuilder<T> = Widget Function(T state);
|
||||
|
||||
typedef StateAndChildWidgetBuilder<T> = Widget Function(T state, Widget? child);
|
||||
|
||||
class LocaleBuilder extends StatelessWidget {
|
||||
final StateWidgetBuilder<String?> builder;
|
||||
|
||||
@@ -86,3 +88,30 @@ class LocaleBuilder extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ActiveBuilder extends StatelessWidget {
|
||||
final String label;
|
||||
final StateAndChildWidgetBuilder<bool> builder;
|
||||
final Widget? child;
|
||||
|
||||
const ActiveBuilder({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.builder,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<AppState, bool>(
|
||||
selector: (_, appState) => appState.currentLabel == label,
|
||||
builder: (_, state, child) {
|
||||
return builder(
|
||||
state,
|
||||
child,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'card.dart';
|
||||
import 'input.dart';
|
||||
import 'sheet.dart';
|
||||
import 'scaffold.dart';
|
||||
import 'sheet.dart';
|
||||
|
||||
class Delegate {
|
||||
const Delegate();
|
||||
@@ -359,15 +359,14 @@ class ListItem<T> extends StatelessWidget {
|
||||
);
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CommonScaffold(
|
||||
|
||||
BaseNavigator.push(
|
||||
context,
|
||||
CommonScaffold(
|
||||
key: Key(nextDelegate.title),
|
||||
body: nextDelegate.widget,
|
||||
title: nextDelegate.title,
|
||||
),
|
||||
),
|
||||
);
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -471,7 +470,7 @@ class ListHeader extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<Widget> generateSection({
|
||||
required String title,
|
||||
String? title,
|
||||
required Iterable<Widget> items,
|
||||
List<Widget>? actions,
|
||||
bool separated = true,
|
||||
@@ -484,7 +483,7 @@ List<Widget> generateSection({
|
||||
)
|
||||
: items;
|
||||
return [
|
||||
if (items.isNotEmpty)
|
||||
if (items.isNotEmpty && title != null)
|
||||
ListHeader(
|
||||
title: title,
|
||||
actions: actions,
|
||||
|
||||
@@ -50,6 +50,7 @@ class CommonScaffold extends StatefulWidget {
|
||||
|
||||
class CommonScaffoldState extends State<CommonScaffold> {
|
||||
final ValueNotifier<List<Widget>> _actions = ValueNotifier([]);
|
||||
final ValueNotifier<dynamic> _floatingActionButton = ValueNotifier(null);
|
||||
final ValueNotifier<bool> _loading = ValueNotifier(false);
|
||||
|
||||
set actions(List<Widget> actions) {
|
||||
@@ -58,6 +59,12 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
}
|
||||
}
|
||||
|
||||
set floatingActionButton(Widget floatingActionButton) {
|
||||
if (_floatingActionButton.value != floatingActionButton) {
|
||||
_floatingActionButton.value = floatingActionButton;
|
||||
}
|
||||
}
|
||||
|
||||
Future<T?> loadingRun<T>(
|
||||
Future<T> Function() futureFunction, {
|
||||
String? title,
|
||||
@@ -82,6 +89,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
@override
|
||||
void dispose() {
|
||||
_actions.dispose();
|
||||
_floatingActionButton.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -90,6 +98,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.title != widget.title) {
|
||||
_actions.value = [];
|
||||
_floatingActionButton.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,60 +108,66 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scaffold = Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
ValueListenableBuilder<List<Widget>>(
|
||||
valueListenable: _actions,
|
||||
builder: (_, actions, __) {
|
||||
final realActions =
|
||||
final scaffold = ValueListenableBuilder(
|
||||
valueListenable: _floatingActionButton,
|
||||
builder: (_, value, __) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
ValueListenableBuilder<List<Widget>>(
|
||||
valueListenable: _actions,
|
||||
builder: (_, actions, __) {
|
||||
final realActions =
|
||||
actions.isNotEmpty ? actions : widget.actions;
|
||||
return AppBar(
|
||||
centerTitle: false,
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness:
|
||||
return AppBar(
|
||||
centerTitle: false,
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarIconBrightness:
|
||||
systemNavigationBarIconBrightness:
|
||||
Theme.of(context).brightness == Brightness.dark
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemNavigationBarColor: widget.bottomNavigationBar != null
|
||||
? context.colorScheme.surfaceContainer
|
||||
: context.colorScheme.surface,
|
||||
systemNavigationBarDividerColor: Colors.transparent,
|
||||
),
|
||||
automaticallyImplyLeading: widget.automaticallyImplyLeading,
|
||||
leading: widget.leading,
|
||||
title: Text(widget.title),
|
||||
actions: [
|
||||
...?realActions,
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
systemNavigationBarColor: widget.bottomNavigationBar != null
|
||||
? context.colorScheme.surfaceContainer
|
||||
: context.colorScheme.surface,
|
||||
systemNavigationBarDividerColor: Colors.transparent,
|
||||
),
|
||||
automaticallyImplyLeading: widget.automaticallyImplyLeading,
|
||||
leading: widget.leading,
|
||||
title: Text(widget.title),
|
||||
actions: [
|
||||
...?realActions,
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _loading,
|
||||
builder: (_, value, __) {
|
||||
return value == true
|
||||
? const LinearProgressIndicator()
|
||||
: Container();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _loading,
|
||||
builder: (_, value, __) {
|
||||
return value == true
|
||||
? const LinearProgressIndicator()
|
||||
: Container();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: body,
|
||||
bottomNavigationBar: widget.bottomNavigationBar,
|
||||
),
|
||||
body: body,
|
||||
floatingActionButton: value,
|
||||
bottomNavigationBar: widget.bottomNavigationBar,
|
||||
);
|
||||
},
|
||||
);
|
||||
return _sideNavigationBar != null
|
||||
? Row(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'side_sheet.dart';
|
||||
|
||||
showExtendPage(
|
||||
@@ -23,12 +24,11 @@ showExtendPage(
|
||||
final isMobile =
|
||||
globalState.appController.appState.viewMode == ViewMode.mobile;
|
||||
if (isMobile) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => CommonScaffold(
|
||||
title: title,
|
||||
body: uniqueBody,
|
||||
),
|
||||
BaseNavigator.push(
|
||||
context,
|
||||
CommonScaffold(
|
||||
title: title,
|
||||
body: uniqueBody,
|
||||
),
|
||||
);
|
||||
return;
|
||||
|
||||
53
lib/widgets/subscription_info_view.dart
Normal file
53
lib/widgets/subscription_info_view.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SubscriptionInfoView extends StatelessWidget {
|
||||
final SubscriptionInfo? subscriptionInfo;
|
||||
|
||||
const SubscriptionInfoView({
|
||||
super.key,
|
||||
this.subscriptionInfo,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (subscriptionInfo == null) {
|
||||
return Container();
|
||||
}
|
||||
if (subscriptionInfo?.total == 0) {
|
||||
return Container();
|
||||
}
|
||||
final use = subscriptionInfo!.upload + subscriptionInfo!.download;
|
||||
final total = subscriptionInfo!.total;
|
||||
final progress = use / total;
|
||||
|
||||
final useShow = TrafficValue(value: use).show;
|
||||
final totalShow = TrafficValue(value: total).show;
|
||||
final expireShow = subscriptionInfo?.expire != null &&
|
||||
subscriptionInfo!.expire != 0
|
||||
? DateTime.fromMillisecondsSinceEpoch(subscriptionInfo!.expire * 1000)
|
||||
.show
|
||||
: appLocalizations.infiniteTime;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
LinearProgressIndicator(
|
||||
minHeight: 6,
|
||||
value: progress,
|
||||
backgroundColor: context.colorScheme.primary.toSoft(),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"$useShow / $totalShow · $expireShow",
|
||||
style: context.textTheme.labelMedium?.toLight,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,25 @@
|
||||
export 'scaffold.dart';
|
||||
export 'float_layout.dart';
|
||||
export 'popup_menu.dart';
|
||||
export 'card.dart';
|
||||
export 'list.dart';
|
||||
export 'line_chart.dart';
|
||||
export 'grid.dart';
|
||||
export 'open_container.dart';
|
||||
export 'color_scheme_box.dart';
|
||||
export 'null_status.dart';
|
||||
export 'disabled_mask.dart';
|
||||
export 'side_sheet.dart';
|
||||
export 'sheet.dart';
|
||||
export 'animate_grid.dart';
|
||||
export 'chip.dart';
|
||||
export 'fade_box.dart';
|
||||
export 'text.dart';
|
||||
export 'connection_item.dart';
|
||||
export 'back_scope.dart';
|
||||
export 'builder.dart';
|
||||
export 'setting.dart';
|
||||
export 'card.dart';
|
||||
export 'chip.dart';
|
||||
export 'color_scheme_box.dart';
|
||||
export 'connection_item.dart';
|
||||
export 'disabled_mask.dart';
|
||||
export 'fade_box.dart';
|
||||
export 'float_layout.dart';
|
||||
export 'grid.dart';
|
||||
export 'icon.dart';
|
||||
export 'input.dart';
|
||||
export 'keep_scope.dart';
|
||||
export 'back_scope.dart';
|
||||
export 'icon.dart';
|
||||
export 'line_chart.dart';
|
||||
export 'list.dart';
|
||||
export 'null_status.dart';
|
||||
export 'open_container.dart';
|
||||
export 'popup_menu.dart';
|
||||
export 'scaffold.dart';
|
||||
export 'setting.dart';
|
||||
export 'sheet.dart';
|
||||
export 'side_sheet.dart';
|
||||
export 'subscription_info_view.dart';
|
||||
export 'text.dart';
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <gtk/gtk_plugin.h>
|
||||
#include <hotkey_manager_linux/hotkey_manager_linux_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
||||
#include <tray_manager/tray_manager_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
@@ -28,9 +28,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) hotkey_manager_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "HotkeyManagerLinuxPlugin");
|
||||
hotkey_manager_linux_plugin_register_with_registrar(hotkey_manager_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
|
||||
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) tray_manager_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin");
|
||||
tray_manager_plugin_register_with_registrar(tray_manager_registrar);
|
||||
|
||||
@@ -7,7 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_linux
|
||||
gtk
|
||||
hotkey_manager_linux
|
||||
screen_retriever
|
||||
screen_retriever_linux
|
||||
tray_manager
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
|
||||
@@ -6,6 +6,7 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import app_links
|
||||
import connectivity_plus
|
||||
import device_info_plus
|
||||
import dynamic_color
|
||||
import file_selector_macos
|
||||
@@ -13,15 +14,17 @@ import hotkey_manager_macos
|
||||
import mobile_scanner
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import screen_retriever
|
||||
import screen_retriever_macos
|
||||
import shared_preferences_foundation
|
||||
import sqflite
|
||||
import sqflite_darwin
|
||||
import tray_manager
|
||||
import url_launcher_macos
|
||||
import window_ext
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
@@ -29,10 +32,11 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WindowExtPlugin.register(with: registry.registrar(forPlugin: "WindowExtPlugin"))
|
||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||
}
|
||||
|
||||
@@ -583,7 +583,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.clash.follow;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.follow.clash;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
@@ -711,7 +711,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.clash.follow;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.follow.clash;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@@ -733,7 +733,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "${SRCROOT}/../libclash/macos/";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.clash.follow;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.follow.clash;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import window_ext
|
||||
|
||||
@NSApplicationMain
|
||||
@main
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
|
||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||
if !flag {
|
||||
for window in NSApp.windows {
|
||||
if !window.isVisible {
|
||||
window.setIsVisible(true)
|
||||
}
|
||||
window.makeKeyAndOrderFront(self)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||
WindowExtPlugin.instance?.handleShouldTerminate()
|
||||
return .terminateCancel
|
||||
}
|
||||
|
||||
override func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||
if !flag {
|
||||
for window in NSApp.windows {
|
||||
if !window.isVisible {
|
||||
window.setIsVisible(true)
|
||||
}
|
||||
window.makeKeyAndOrderFront(self)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string></string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
|
||||
@@ -8,9 +8,8 @@ class MainFlutterWindow: NSWindow {
|
||||
let windowFrame = self.frame
|
||||
self.contentViewController = flutterViewController
|
||||
self.setFrame(windowFrame, display: true)
|
||||
|
||||
|
||||
RegisterGeneratedPlugins(registry: flutterViewController)
|
||||
|
||||
super.awakeFromNib()
|
||||
}
|
||||
override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) {
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'proxy_platform_interface.dart';
|
||||
import "package:path/path.dart";
|
||||
|
||||
import 'proxy_platform_interface.dart';
|
||||
|
||||
enum ProxyTypes { http, https, socks }
|
||||
|
||||
class Proxy extends ProxyPlatform {
|
||||
static String url = "127.0.0.1";
|
||||
|
||||
@override
|
||||
Future<bool?> startProxy(int port) async {
|
||||
Future<bool?> startProxy(
|
||||
int port, [
|
||||
List<String> bypassDomain = const [],
|
||||
]) async {
|
||||
return switch (Platform.operatingSystem) {
|
||||
"macos" => await _startProxyWithMacos(port),
|
||||
"linux" => await _startProxyWithLinux(port),
|
||||
"windows" => await ProxyPlatform.instance.startProxy(port),
|
||||
"macos" => await _startProxyWithMacos(port, bypassDomain),
|
||||
"linux" => await _startProxyWithLinux(port, bypassDomain),
|
||||
"windows" => await ProxyPlatform.instance.startProxy(port, bypassDomain),
|
||||
String() => false,
|
||||
};
|
||||
}
|
||||
@@ -28,48 +32,93 @@ class Proxy extends ProxyPlatform {
|
||||
};
|
||||
}
|
||||
|
||||
Future<bool> _startProxyWithLinux(int port) async {
|
||||
Future<bool> _startProxyWithLinux(int port, List<String> bypassDomain) async {
|
||||
try {
|
||||
final homeDir = Platform.environment['HOME']!;
|
||||
final configDir = join(homeDir, ".config");
|
||||
final cmdList = List<List<String>>.empty(growable: true);
|
||||
final desktop = Platform.environment['XDG_CURRENT_DESKTOP'];
|
||||
final isKDE = desktop == "KDE";
|
||||
for (final type in ProxyTypes.values) {
|
||||
if (isKDE) {
|
||||
cmdList.add(
|
||||
[
|
||||
"kwriteconfig5",
|
||||
"--file",
|
||||
"$configDir/kioslaverc",
|
||||
"--group",
|
||||
"Proxy Settings",
|
||||
"--key",
|
||||
"ProxyType",
|
||||
"1"
|
||||
],
|
||||
);
|
||||
cmdList.add(
|
||||
[
|
||||
"kwriteconfig5",
|
||||
"--file",
|
||||
"$configDir/kioslaverc",
|
||||
"--group",
|
||||
"Proxy Settings",
|
||||
"--key",
|
||||
"NoProxyFor",
|
||||
bypassDomain.join(",")
|
||||
],
|
||||
);
|
||||
} else {
|
||||
cmdList.add(
|
||||
["gsettings", "set", "org.gnome.system.proxy", "mode", "manual"],
|
||||
);
|
||||
final ignoreHosts = "\"['${bypassDomain.join("', '")}']\"";
|
||||
cmdList.add(
|
||||
[
|
||||
"gsettings",
|
||||
"set",
|
||||
"org.gnome.system.proxy.${type.name}",
|
||||
"host",
|
||||
url
|
||||
"org.gnome.system.proxy",
|
||||
"ignore-hosts",
|
||||
ignoreHosts
|
||||
],
|
||||
);
|
||||
cmdList.add(
|
||||
[
|
||||
"gsettings",
|
||||
"set",
|
||||
"org.gnome.system.proxy.${type.name}",
|
||||
"port",
|
||||
"$port"
|
||||
],
|
||||
);
|
||||
if (isKDE) {
|
||||
}
|
||||
for (final type in ProxyTypes.values) {
|
||||
if (!isKDE) {
|
||||
cmdList.add(
|
||||
[
|
||||
"kwriteconfig5",
|
||||
"--file",
|
||||
"$configDir/kioslaverc",
|
||||
"--group",
|
||||
"Proxy Settings",
|
||||
"--key",
|
||||
"ProxyType",
|
||||
"1"
|
||||
"gsettings",
|
||||
"set",
|
||||
"org.gnome.system.proxy.${type.name}",
|
||||
"host",
|
||||
url
|
||||
],
|
||||
);
|
||||
cmdList.add(
|
||||
[
|
||||
"gsettings",
|
||||
"set",
|
||||
"org.gnome.system.proxy.${type.name}",
|
||||
"port",
|
||||
"$port"
|
||||
],
|
||||
);
|
||||
cmdList.add(
|
||||
[
|
||||
"gsettings",
|
||||
"set",
|
||||
"org.gnome.system.proxy.${type.name}",
|
||||
"port",
|
||||
"$port"
|
||||
],
|
||||
);
|
||||
cmdList.add(
|
||||
[
|
||||
"gsettings",
|
||||
"set",
|
||||
"org.gnome.system.proxy.${type.name}",
|
||||
"port",
|
||||
"$port"
|
||||
],
|
||||
);
|
||||
}
|
||||
if (isKDE) {
|
||||
cmdList.add(
|
||||
[
|
||||
"kwriteconfig5",
|
||||
@@ -100,19 +149,23 @@ class Proxy extends ProxyPlatform {
|
||||
final cmdList = List<List<String>>.empty(growable: true);
|
||||
final desktop = Platform.environment['XDG_CURRENT_DESKTOP'];
|
||||
final isKDE = desktop == "KDE";
|
||||
cmdList
|
||||
.add(["gsettings", "set", "org.gnome.system.proxy", "mode", "none"]);
|
||||
if (isKDE) {
|
||||
cmdList.add([
|
||||
"kwriteconfig5",
|
||||
"--file",
|
||||
"$configDir/kioslaverc",
|
||||
"--group",
|
||||
"Proxy Settings",
|
||||
"--key",
|
||||
"ProxyType",
|
||||
"0"
|
||||
]);
|
||||
cmdList.add(
|
||||
[
|
||||
"kwriteconfig5",
|
||||
"--file",
|
||||
"$configDir/kioslaverc",
|
||||
"--group",
|
||||
"Proxy Settings",
|
||||
"--key",
|
||||
"ProxyType",
|
||||
"0"
|
||||
],
|
||||
);
|
||||
} else {
|
||||
cmdList.add(
|
||||
["gsettings", "set", "org.gnome.system.proxy", "mode", "none"],
|
||||
);
|
||||
}
|
||||
for (final cmd in cmdList) {
|
||||
await Process.run(cmd[0], cmd.sublist(1));
|
||||
@@ -123,23 +176,43 @@ class Proxy extends ProxyPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _startProxyWithMacos(int port) async {
|
||||
Future<bool> _startProxyWithMacos(int port, List<String> bypassDomain) async {
|
||||
try {
|
||||
final devices = await _getNetworkDeviceListWithMacos();
|
||||
for (final dev in devices) {
|
||||
await Future.wait([
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup", ["-setwebproxystate", dev, "on"]),
|
||||
"/usr/sbin/networksetup",
|
||||
["-setwebproxystate", dev, "on"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup", ["-setwebproxy", dev, url, "$port"]),
|
||||
"/usr/sbin/networksetup",
|
||||
["-setwebproxy", dev, url, "$port"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup", ["-setsecurewebproxystate", dev, "on"]),
|
||||
Process.run("/usr/sbin/networksetup",
|
||||
["-setsecurewebproxy", dev, url, "$port"]),
|
||||
Process.run("/usr/sbin/networksetup",
|
||||
["-setsocksfirewallproxystate", dev, "on"]),
|
||||
Process.run("/usr/sbin/networksetup",
|
||||
["-setsocksfirewallproxy", dev, url, "$port"]),
|
||||
"/usr/sbin/networksetup",
|
||||
["-setsecurewebproxystate", dev, "on"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup",
|
||||
["-setsecurewebproxy", dev, url, "$port"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup",
|
||||
["-setsocksfirewallproxystate", dev, "on"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup",
|
||||
["-setsocksfirewallproxy", dev, url, "$port"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup",
|
||||
[
|
||||
"-setproxybypassdomains",
|
||||
dev,
|
||||
bypassDomain.join(" "),
|
||||
],
|
||||
),
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
@@ -154,13 +227,25 @@ class Proxy extends ProxyPlatform {
|
||||
for (final dev in devices) {
|
||||
await Future.wait([
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup", ["-setautoproxystate", dev, "off"]),
|
||||
"/usr/sbin/networksetup",
|
||||
["-setautoproxystate", dev, "off"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup", ["-setwebproxystate", dev, "off"]),
|
||||
Process.run("/usr/sbin/networksetup",
|
||||
["-setsecurewebproxystate", dev, "off"]),
|
||||
Process.run("/usr/sbin/networksetup",
|
||||
["-setsocksfirewallproxystate", dev, "off"]),
|
||||
"/usr/sbin/networksetup",
|
||||
["-setwebproxystate", dev, "off"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup",
|
||||
["-setsecurewebproxystate", dev, "off"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup",
|
||||
["-setsocksfirewallproxystate", dev, "off"],
|
||||
),
|
||||
Process.run(
|
||||
"/usr/sbin/networksetup",
|
||||
["-setproxybypassdomains", dev, ""],
|
||||
),
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -12,12 +12,15 @@ class MethodChannelProxy extends ProxyPlatform {
|
||||
MethodChannelProxy();
|
||||
|
||||
@override
|
||||
Future<bool?> startProxy(int port) async {
|
||||
return await methodChannel.invokeMethod<bool>("StartProxy", {'port': port});
|
||||
Future<bool?> startProxy(int port, List<String> bypassDomain) async {
|
||||
return await methodChannel.invokeMethod<bool>("StartProxy", {
|
||||
'port': port,
|
||||
'bypassDomain': bypassDomain,
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool?> stopProxy() async {
|
||||
return await methodChannel.invokeMethod<bool>("StopProxy");
|
||||
return await methodChannel.invokeMethod<bool>("StopProxy");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ abstract class ProxyPlatform extends PlatformInterface {
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Future<bool?> startProxy(int port) {
|
||||
Future<bool?> startProxy(int port, List<String> bypassDomain) {
|
||||
throw UnimplementedError('startProxy() has not been implemented.');
|
||||
}
|
||||
|
||||
|
||||
@@ -22,18 +22,32 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
void startProxy(const int port)
|
||||
void startProxy(const int port, const flutter::EncodableList& bypassDomain)
|
||||
{
|
||||
INTERNET_PER_CONN_OPTION_LIST list;
|
||||
DWORD dwBufSize = sizeof(list);
|
||||
list.dwSize = sizeof(list);
|
||||
list.pszConnection = nullptr;
|
||||
|
||||
auto url = "127.0.0.1:" + std::to_string(port);
|
||||
auto wUrl = std::wstring(url.begin(), url.end());
|
||||
auto fullAddr = new WCHAR[url.length() + 1];
|
||||
wcscpy_s(fullAddr, url.length() + 1, wUrl.c_str());
|
||||
list.dwOptionCount = 2;
|
||||
list.pOptions = new INTERNET_PER_CONN_OPTION[2];
|
||||
|
||||
std::wstring wBypassList;
|
||||
|
||||
for (const auto& domain : bypassDomain) {
|
||||
if (!wBypassList.empty()) {
|
||||
wBypassList += L";";
|
||||
}
|
||||
wBypassList += std::wstring(std::get<std::string>(domain).begin(), std::get<std::string>(domain).end());
|
||||
}
|
||||
|
||||
auto bypassAddr = new WCHAR[wBypassList.length() + 1];
|
||||
wcscpy_s(bypassAddr, wBypassList.length() + 1, wBypassList.c_str());
|
||||
|
||||
list.dwOptionCount = 3;
|
||||
list.pOptions = new INTERNET_PER_CONN_OPTION[3];
|
||||
|
||||
if (!list.pOptions)
|
||||
{
|
||||
@@ -46,6 +60,9 @@ void startProxy(const int port)
|
||||
list.pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
|
||||
list.pOptions[1].Value.pszValue = fullAddr;
|
||||
|
||||
list.pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
|
||||
list.pOptions[2].Value.pszValue = bypassAddr;
|
||||
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
|
||||
|
||||
RASENTRYNAME entry;
|
||||
@@ -70,7 +87,11 @@ void startProxy(const int port)
|
||||
list.pszConnection = entryAddr[i].szEntryName;
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);
|
||||
}
|
||||
|
||||
delete[] fullAddr;
|
||||
delete[] bypassAddr;
|
||||
delete[] list.pOptions;
|
||||
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);
|
||||
InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);
|
||||
}
|
||||
@@ -160,7 +181,8 @@ namespace proxy
|
||||
{
|
||||
auto *arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
|
||||
auto port = std::get<int>(arguments->at(flutter::EncodableValue("port")));
|
||||
startProxy(port);
|
||||
auto bypassDomain = std::get<flutter::EncodableList>(arguments->at(flutter::EncodableValue("bypassDomain")));
|
||||
startProxy(port, bypassDomain);
|
||||
result->Success(true);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "b0850beeb25f6d5b10426284f506557f66181b36"
|
||||
revision: "603104015dd692ea3403755b55d07813d5cf8965"
|
||||
channel: "stable"
|
||||
|
||||
project_type: plugin
|
||||
@@ -13,11 +13,14 @@ project_type: plugin
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||
create_revision: 603104015dd692ea3403755b55d07813d5cf8965
|
||||
base_revision: 603104015dd692ea3403755b55d07813d5cf8965
|
||||
- platform: macos
|
||||
create_revision: 603104015dd692ea3403755b55d07813d5cf8965
|
||||
base_revision: 603104015dd692ea3403755b55d07813d5cf8965
|
||||
- platform: windows
|
||||
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||
create_revision: 603104015dd692ea3403755b55d07813d5cf8965
|
||||
base_revision: 603104015dd692ea3403755b55d07813d5cf8965
|
||||
|
||||
# User provided section
|
||||
|
||||
|
||||
43
plugins/window_ext/example/.gitignore
vendored
43
plugins/window_ext/example/.gitignore
vendored
@@ -1,43 +0,0 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
@@ -1,16 +0,0 @@
|
||||
# window_ext_example
|
||||
|
||||
Demonstrates how to use the window_ext plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
@@ -1,28 +0,0 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -1,25 +0,0 @@
|
||||
// This is a basic Flutter integration test.
|
||||
//
|
||||
// Since integration tests run in a full Flutter application, they can interact
|
||||
// with the host side of a plugin implementation, unlike Dart unit tests.
|
||||
//
|
||||
// For more information about Flutter integration tests, please see
|
||||
// https://docs.flutter.dev/cookbook/testing/integration/introduction
|
||||
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'package:window_ext/window_ext.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
|
||||
final WindowExt plugin = WindowExt();
|
||||
final String? version = await plugin.getPlatformVersion();
|
||||
// The version string depends on the host platform running the test, so
|
||||
// just assert that some non-empty string is returned.
|
||||
expect(version?.isNotEmpty, true);
|
||||
});
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user