Compare commits

..

5 Commits

Author SHA1 Message Date
chen08209
a333794ceb Fix android tile issues 2024-11-17 21:16:14 +08:00
chen08209
d89481114f Fix windows tray issues
Support setting bypassDomain

Update flutter version

Fix android service issues

Fix macos dock exit button issues

Add route address setting

Optimize provider view
2024-11-17 20:42:01 +08:00
chen08209
9dcb53b2fe Update changelog 2024-11-09 20:33:16 +00:00
chen08209
be4eaf4d52 Update CHANGELOG.md 2024-11-10 04:32:46 +08:00
chen08209
72bef5f672 Add android shortcuts
Fix init params issues

Fix dynamic color issues

Optimize navigator animate

Optimize window init

Optimize fab

Optimize save
2024-11-09 17:42:08 +08:00
131 changed files with 8205 additions and 8519 deletions

View File

@@ -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

View File

@@ -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 }}

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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)
}
})
}
}

View File

@@ -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,

View File

@@ -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() {

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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() {

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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
View 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,
})
}
}

View File

@@ -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(),
}

View File

@@ -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"`
}

View File

@@ -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)
}
}
})

View File

@@ -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();
});
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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';

View File

@@ -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
View 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,
),
);
}
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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(

View File

@@ -173,3 +173,8 @@ enum FontFamily {
const FontFamily([this.value]);
}
enum RouteMode {
bypassPrivate,
config,
}

View File

@@ -24,6 +24,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
title: appLocalizations.network,
isScaffold: true,
isBlur: false,
extendPageWidth: 360,
widget: const NetworkListView(),
),
),

View File

@@ -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(),
],
),
];

View File

@@ -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(

View File

@@ -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();
}
}

View File

@@ -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,
);
},
),
);
}

View File

@@ -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,

View File

@@ -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),

View 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,

View File

@@ -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(

View File

@@ -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(

View File

@@ -191,7 +191,7 @@ class ProxiesSetting extends StatelessWidget {
_buildGroupStyleSetting() {
return generateSection(
title: "图标样式",
title: appLocalizations.iconStyle,
items: [
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16),

View File

@@ -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,
);
}
}

View File

@@ -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,

View File

@@ -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"
}

View File

@@ -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": "配置监听路由地址"
}

View File

@@ -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":

View File

@@ -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("白名单模式"),

View File

@@ -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> {

View File

@@ -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(

View File

@@ -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,
),
);
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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,

View File

@@ -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();
}

View File

@@ -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}';
}
}

View File

@@ -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;
}
}

View File

@@ -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}';
}
}

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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(),

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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';

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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);

View File

@@ -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,
);
}
}

View File

@@ -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,

View File

@@ -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(

View File

@@ -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;

View 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,
),
],
);
}
}

View File

@@ -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';

View File

@@ -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);

View File

@@ -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

View File

@@ -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"))
}

View File

@@ -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;
};

View File

@@ -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
}
}

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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");
}
}

View File

@@ -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.');
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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