Compare commits
1 Commits
v0.8.87
...
v0.8.83-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30ee6889ab |
8
.github/workflows/build.yaml
vendored
8
.github/workflows/build.yaml
vendored
@@ -211,6 +211,14 @@ jobs:
|
||||
version=$(echo "${{ github.ref_name }}" | sed 's/^v//')
|
||||
sed "s|VERSION|$version|g" ./.github/release_template.md >> release.md
|
||||
|
||||
- name: Generate sha256
|
||||
if: env.IS_STABLE == 'true'
|
||||
run: |
|
||||
cd ./dist
|
||||
for file in $(find . -type f -not -name "*.sha256"); do
|
||||
sha256sum "$file" > "${file}.sha256"
|
||||
done
|
||||
|
||||
- name: Release
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
uses: softprops/action-gh-release@v2
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||
|
||||
@@ -20,6 +20,10 @@ enum class RunState {
|
||||
object GlobalState {
|
||||
val runLock = ReentrantLock()
|
||||
|
||||
const val NOTIFICATION_CHANNEL = "FlClash"
|
||||
|
||||
const val NOTIFICATION_ID = 1
|
||||
|
||||
val runState: MutableLiveData<RunState> = MutableLiveData<RunState>(RunState.STOP)
|
||||
var flutterEngine: FlutterEngine? = null
|
||||
private var serviceEngine: FlutterEngine? = null
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.follow.clash
|
||||
|
||||
import com.follow.clash.core.Core
|
||||
import com.follow.clash.plugins.AppPlugin
|
||||
import com.follow.clash.plugins.ServicePlugin
|
||||
import com.follow.clash.plugins.TilePlugin
|
||||
|
||||
@@ -27,7 +27,6 @@ import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
||||
suspend fun Drawable.getBase64(): String {
|
||||
val drawable = this
|
||||
return withContext(Dispatchers.IO) {
|
||||
|
||||
@@ -168,8 +168,10 @@ data object VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
|
||||
try {
|
||||
if (GlobalState.runState.value != RunState.START) return
|
||||
val data = flutterMethodChannel.awaitResult<String>("getStartForegroundParams")
|
||||
val startForegroundParams = Gson().fromJson(
|
||||
val startForegroundParams = if (data != null) Gson().fromJson(
|
||||
data, StartForegroundParams::class.java
|
||||
) else StartForegroundParams(
|
||||
title = "", content = ""
|
||||
)
|
||||
if (lastStartForegroundParams != startForegroundParams) {
|
||||
lastStartForegroundParams = startForegroundParams
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
package com.follow.clash.services
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Notification
|
||||
import android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.MainActivity
|
||||
import com.follow.clash.R
|
||||
import com.follow.clash.extensions.getActionPendingIntent
|
||||
import com.follow.clash.models.VpnOptions
|
||||
import io.flutter.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
|
||||
interface BaseServiceInterface {
|
||||
|
||||
@@ -9,4 +29,71 @@ interface BaseServiceInterface {
|
||||
fun stop()
|
||||
|
||||
suspend fun startForeground(title: String, content: String)
|
||||
}
|
||||
|
||||
fun Service.createFlClashNotificationBuilder(): Deferred<NotificationCompat.Builder> =
|
||||
CoroutineScope(Dispatchers.Main).async {
|
||||
val stopText = GlobalState.getText("stop")
|
||||
val intent = Intent(this@createFlClashNotificationBuilder, MainActivity::class.java)
|
||||
|
||||
val pendingIntent = if (Build.VERSION.SDK_INT >= 31) {
|
||||
PendingIntent.getActivity(
|
||||
this@createFlClashNotificationBuilder,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
} else {
|
||||
PendingIntent.getActivity(
|
||||
this@createFlClashNotificationBuilder, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
|
||||
with(
|
||||
NotificationCompat.Builder(
|
||||
this@createFlClashNotificationBuilder, GlobalState.NOTIFICATION_CHANNEL
|
||||
)
|
||||
) {
|
||||
setSmallIcon(R.drawable.ic_stat_name)
|
||||
setContentTitle("FlClash")
|
||||
setContentIntent(pendingIntent)
|
||||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
|
||||
}
|
||||
setOngoing(true)
|
||||
addAction(
|
||||
0, stopText, getActionPendingIntent("STOP")
|
||||
)
|
||||
setShowWhen(false)
|
||||
setOnlyAlertOnce(true)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ForegroundServiceType")
|
||||
fun Service.startForeground(notification: Notification) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
var channel = manager?.getNotificationChannel(GlobalState.NOTIFICATION_CHANNEL)
|
||||
if (channel == null) {
|
||||
Log.d("[FlClash]","createNotificationChannel===>")
|
||||
channel = NotificationChannel(
|
||||
GlobalState.NOTIFICATION_CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
manager?.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("[FlClash]","startForeground===>")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
try {
|
||||
startForeground(
|
||||
GlobalState.NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
startForeground(GlobalState.NOTIFICATION_ID, notification)
|
||||
}
|
||||
} else {
|
||||
startForeground(GlobalState.NOTIFICATION_ID, notification)
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,51 @@
|
||||
package com.follow.clash.services
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationCompat
|
||||
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.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
|
||||
|
||||
class FlClashService : Service(), BaseServiceInterface {
|
||||
|
||||
override fun start(options: VpnOptions) = 0
|
||||
|
||||
override fun stop() {
|
||||
stopSelf()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
}
|
||||
}
|
||||
|
||||
private var cachedBuilder: NotificationCompat.Builder? = null
|
||||
|
||||
private suspend fun notificationBuilder(): NotificationCompat.Builder {
|
||||
if (cachedBuilder == null) {
|
||||
cachedBuilder = createFlClashNotificationBuilder().await()
|
||||
}
|
||||
return cachedBuilder!!
|
||||
}
|
||||
|
||||
@SuppressLint("ForegroundServiceType")
|
||||
override suspend fun startForeground(title: String, content: String) {
|
||||
startForeground(
|
||||
notificationBuilder()
|
||||
.setContentTitle(title)
|
||||
.setContentText(content).build()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
super.onTrimMemory(level)
|
||||
GlobalState.getCurrentVPNPlugin()?.requestGc()
|
||||
}
|
||||
|
||||
|
||||
private val binder = LocalBinder()
|
||||
|
||||
inner class LocalBinder : Binder() {
|
||||
@@ -38,93 +60,8 @@ class FlClashService : Service(), BaseServiceInterface {
|
||||
return super.onUnbind(intent)
|
||||
}
|
||||
|
||||
private val CHANNEL = "FlClash"
|
||||
|
||||
private val notificationId: Int = 1
|
||||
|
||||
private val notificationBuilderDeferred: Deferred<NotificationCompat.Builder> by lazy {
|
||||
CoroutineScope(Dispatchers.Main).async {
|
||||
val stopText = GlobalState.getText("stop")
|
||||
|
||||
val intent = Intent(
|
||||
this@FlClashService, MainActivity::class.java
|
||||
)
|
||||
|
||||
val pendingIntent = if (Build.VERSION.SDK_INT >= 31) {
|
||||
PendingIntent.getActivity(
|
||||
this@FlClashService,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
} else {
|
||||
PendingIntent.getActivity(
|
||||
this@FlClashService,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
|
||||
with(NotificationCompat.Builder(this@FlClashService, CHANNEL)) {
|
||||
setSmallIcon(com.follow.clash.R.drawable.ic_stat_name)
|
||||
setContentTitle("FlClash")
|
||||
setContentIntent(pendingIntent)
|
||||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
priority = NotificationCompat.PRIORITY_MIN
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
|
||||
}
|
||||
addAction(
|
||||
0,
|
||||
stopText, // 使用 suspend 函数获取的文本
|
||||
getActionPendingIntent("STOP")
|
||||
)
|
||||
setOngoing(true)
|
||||
setShowWhen(false)
|
||||
setOnlyAlertOnce(true)
|
||||
setAutoCancel(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getNotificationBuilder(): NotificationCompat.Builder {
|
||||
return notificationBuilderDeferred.await()
|
||||
}
|
||||
|
||||
override fun start(options: VpnOptions) = 0
|
||||
|
||||
override fun stop() {
|
||||
stopSelf()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("ForegroundServiceType")
|
||||
override suspend 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)
|
||||
}
|
||||
}
|
||||
val notification =
|
||||
getNotificationBuilder()
|
||||
.setContentTitle(title)
|
||||
.setContentText(content).build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
try {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} catch (_: Exception) {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
override fun onDestroy() {
|
||||
stop()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
package com.follow.clash.services
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
import android.net.ProxyInfo
|
||||
import android.net.VpnService
|
||||
import android.os.Binder
|
||||
@@ -17,18 +13,13 @@ import android.os.RemoteException
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.MainActivity
|
||||
import com.follow.clash.R
|
||||
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
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@@ -128,82 +119,22 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private val CHANNEL = "FlClash"
|
||||
private var cachedBuilder: NotificationCompat.Builder? = null
|
||||
|
||||
private val notificationId: Int = 1
|
||||
|
||||
private val notificationBuilderDeferred: Deferred<NotificationCompat.Builder> by lazy {
|
||||
CoroutineScope(Dispatchers.Main).async {
|
||||
val stopText = GlobalState.getText("stop")
|
||||
val intent = Intent(this@FlClashVpnService, MainActivity::class.java)
|
||||
|
||||
val pendingIntent = if (Build.VERSION.SDK_INT >= 31) {
|
||||
PendingIntent.getActivity(
|
||||
this@FlClashVpnService,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
} else {
|
||||
PendingIntent.getActivity(
|
||||
this@FlClashVpnService,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
|
||||
with(NotificationCompat.Builder(this@FlClashVpnService, CHANNEL)) {
|
||||
setSmallIcon(R.drawable.ic_stat_name)
|
||||
setContentTitle("FlClash")
|
||||
setContentIntent(pendingIntent)
|
||||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
priority = NotificationCompat.PRIORITY_MIN
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
|
||||
}
|
||||
setOngoing(true)
|
||||
addAction(
|
||||
0,
|
||||
stopText,
|
||||
getActionPendingIntent("STOP")
|
||||
)
|
||||
setShowWhen(false)
|
||||
setOnlyAlertOnce(true)
|
||||
setAutoCancel(true)
|
||||
}
|
||||
private suspend fun notificationBuilder(): NotificationCompat.Builder {
|
||||
if (cachedBuilder == null) {
|
||||
cachedBuilder = createFlClashNotificationBuilder().await()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getNotificationBuilder(): NotificationCompat.Builder {
|
||||
return notificationBuilderDeferred.await()
|
||||
return cachedBuilder!!
|
||||
}
|
||||
|
||||
@SuppressLint("ForegroundServiceType")
|
||||
override suspend 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)
|
||||
}
|
||||
}
|
||||
val notification =
|
||||
getNotificationBuilder()
|
||||
startForeground(
|
||||
notificationBuilder()
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
try {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} catch (_: Exception) {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
.setContentText(content).build()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
|
||||
Submodule core/Clash.Meta updated: f19dad529f...88a1848dfb
@@ -166,6 +166,9 @@ func handleAction(action *Action, result func(data interface{})) {
|
||||
data := action.Data.(string)
|
||||
handleSetState(data)
|
||||
result(true)
|
||||
case crashMethod:
|
||||
result(true)
|
||||
handleCrash()
|
||||
default:
|
||||
handle := nextHandle(action, result)
|
||||
if handle {
|
||||
|
||||
@@ -78,7 +78,6 @@ func getRawConfigWithId(id string) *config.RawConfig {
|
||||
path := getProfilePath(id)
|
||||
bytes, err := readFile(path)
|
||||
if err != nil {
|
||||
log.Errorln("profile is not exist")
|
||||
return config.DefaultRawConfig()
|
||||
}
|
||||
prof, err := config.UnmarshalRawConfig(bytes)
|
||||
|
||||
@@ -82,6 +82,7 @@ const (
|
||||
getRunTimeMethod Method = "getRunTime"
|
||||
getCurrentProfileNameMethod Method = "getCurrentProfileName"
|
||||
getProfileMethod Method = "getProfile"
|
||||
crashMethod Method = "crash"
|
||||
)
|
||||
|
||||
type Method string
|
||||
|
||||
13
core/go.mod
13
core/go.mod
@@ -7,6 +7,7 @@ replace github.com/metacubex/mihomo => ./Clash.Meta
|
||||
require (
|
||||
github.com/metacubex/mihomo v0.0.0-00010101000000-000000000000
|
||||
github.com/samber/lo v1.49.1
|
||||
golang.org/x/sync v0.11.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -52,20 +53,20 @@ require (
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab // indirect
|
||||
github.com/metacubex/bart v0.19.0 // indirect
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 // indirect
|
||||
github.com/metacubex/chacha v0.1.1 // indirect
|
||||
github.com/metacubex/chacha v0.1.2 // indirect
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
|
||||
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect
|
||||
github.com/metacubex/randv2 v0.2.0 // indirect
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 // indirect
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect
|
||||
github.com/metacubex/sing-quic v0.0.0-20250404030904-b2cc8aab562c // indirect
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04 // indirect
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250412122235-0e9005731a63 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250412144348-c426cb167db5 // indirect
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 // indirect
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect
|
||||
github.com/metacubex/utls v1.6.8-alpha.4 // indirect
|
||||
github.com/metacubex/utls v1.7.0-alpha.1 // indirect
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
|
||||
@@ -84,7 +85,6 @@ require (
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||
github.com/sagernet/sing v0.5.2 // indirect
|
||||
github.com/sagernet/sing-mux v0.2.1 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.5 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
|
||||
@@ -107,7 +107,6 @@ require (
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
|
||||
22
core/go.sum
22
core/go.sum
@@ -101,8 +101,8 @@ github.com/metacubex/bart v0.19.0 h1:XQ9AJeI+WO+phRPkUOoflAFwlqDJnm5BPQpixciJQBY
|
||||
github.com/metacubex/bart v0.19.0/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
|
||||
github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c=
|
||||
github.com/metacubex/chacha v0.1.1/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
|
||||
github.com/metacubex/chacha v0.1.2 h1:QulCq3eVm3TO6+4nVIWJtmSe7BT2GMrgVHuAoqRQnlc=
|
||||
github.com/metacubex/chacha v0.1.2/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b h1:RUh4OdVPz/jDrM9MQ2ySuqu2aeBqcA8rtfWUYLZ8RtI=
|
||||
@@ -111,24 +111,24 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j
|
||||
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
|
||||
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
|
||||
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs=
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250404030904-b2cc8aab562c h1:OB3WmMA8YPJjE36RjD9X8xlrWGJ4orxbf2R/KAE28b0=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250404030904-b2cc8aab562c/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q=
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04 h1:B211C+i/I8CWf4I/BaAV0mmkEHrDBJ0XR9EWxjPbFEg=
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250412122235-0e9005731a63 h1:vy/8ZYYtWUXYnOnw/NF8ThG1W/RqM/h5rkun+OXZMH0=
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250412122235-0e9005731a63/go.mod h1:eDZ2JpkSkewGmUlCoLSn2MRFn1D0jKPIys/6aogFx7U=
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250412144348-c426cb167db5 h1:hcsz5e5lqhBxn3iQQDIF60FLZ8PQT542GTQZ+1wcIGo=
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250412144348-c426cb167db5/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 h1:zZp5uct9+/0Hb1jKGyqDjCU4/72t43rs7qOq3Rc9oU8=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
github.com/metacubex/utls v1.6.8-alpha.4 h1:5EvsCHxDNneaOtAyc8CztoNSpmonLvkvuGs01lIeeEI=
|
||||
github.com/metacubex/utls v1.6.8-alpha.4/go.mod h1:MEZ5WO/VLKYs/s/dOzEK/mlXOQxc04ESeLzRgjmLYtk=
|
||||
github.com/metacubex/utls v1.7.0-alpha.1 h1:oMFsPh2oTlALJ7vKXPJuqgy0YeiZ+q/LLw+ZdxZ80l4=
|
||||
github.com/metacubex/utls v1.7.0-alpha.1/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
@@ -174,8 +174,6 @@ github.com/sagernet/sing v0.5.2 h1:2OZQJNKGtji/66QLxbf/T/dqtK/3+fF/zuHH9tsGK7M=
|
||||
github.com/sagernet/sing v0.5.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo=
|
||||
github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE=
|
||||
github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo=
|
||||
github.com/sagernet/sing-shadowtls v0.1.5/go.mod h1:tvrDPTGLrSM46Wnf7mSr+L8NHvgvF8M4YnJF790rZX4=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
|
||||
@@ -442,6 +442,10 @@ func handleSetState(params string) {
|
||||
_ = json.Unmarshal([]byte(params), state.CurrentState)
|
||||
}
|
||||
|
||||
func handleCrash() {
|
||||
panic("handle invoke crash")
|
||||
}
|
||||
|
||||
func init() {
|
||||
adapter.UrlTestHook = func(url string, name string, delay uint16) {
|
||||
delayData := &Delay{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/l10n/l10n.dart';
|
||||
@@ -14,7 +13,6 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'controller.dart';
|
||||
import 'models/models.dart';
|
||||
import 'pages/pages.dart';
|
||||
|
||||
class Application extends ConsumerStatefulWidget {
|
||||
@@ -27,7 +25,6 @@ class Application extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class ApplicationState extends ConsumerState<Application> {
|
||||
late ColorSchemes systemColorSchemes;
|
||||
Timer? _autoUpdateGroupTaskTimer;
|
||||
Timer? _autoUpdateProfilesTaskTimer;
|
||||
|
||||
@@ -132,19 +129,6 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
);
|
||||
}
|
||||
|
||||
_updateSystemColorSchemes(
|
||||
ColorScheme? lightDynamic,
|
||||
ColorScheme? darkDynamic,
|
||||
) {
|
||||
systemColorSchemes = ColorSchemes(
|
||||
lightColorScheme: lightDynamic,
|
||||
darkColorScheme: darkDynamic,
|
||||
);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
globalState.appController.updateSystemColorSchemes(systemColorSchemes);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
return _buildPlatformState(
|
||||
@@ -154,49 +138,44 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
final locale =
|
||||
ref.watch(appSettingProvider.select((state) => state.locale));
|
||||
final themeProps = ref.watch(themeSettingProvider);
|
||||
return DynamicColorBuilder(
|
||||
builder: (lightDynamic, darkDynamic) {
|
||||
_updateSystemColorSchemes(lightDynamic, darkDynamic);
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
navigatorKey: globalState.navigatorKey,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate
|
||||
],
|
||||
builder: (_, child) {
|
||||
return AppEnvManager(
|
||||
child: _buildPlatformApp(
|
||||
_buildApp(child!),
|
||||
),
|
||||
);
|
||||
},
|
||||
scrollBehavior: BaseScrollBehavior(),
|
||||
title: appName,
|
||||
locale: utils.getLocaleForString(locale),
|
||||
supportedLocales: AppLocalizations.delegate.supportedLocales,
|
||||
themeMode: themeProps.themeMode,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: _pageTransitionsTheme,
|
||||
colorScheme: _getAppColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primaryColor: themeProps.primaryColor,
|
||||
),
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
navigatorKey: globalState.navigatorKey,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate
|
||||
],
|
||||
builder: (_, child) {
|
||||
return AppEnvManager(
|
||||
child: _buildPlatformApp(
|
||||
_buildApp(child!),
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: _pageTransitionsTheme,
|
||||
colorScheme: _getAppColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: themeProps.primaryColor,
|
||||
).toPureBlack(themeProps.pureBlack),
|
||||
),
|
||||
home: child,
|
||||
);
|
||||
},
|
||||
scrollBehavior: BaseScrollBehavior(),
|
||||
title: appName,
|
||||
locale: utils.getLocaleForString(locale),
|
||||
supportedLocales: AppLocalizations.delegate.supportedLocales,
|
||||
themeMode: themeProps.themeMode,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: _pageTransitionsTheme,
|
||||
colorScheme: _getAppColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primaryColor: themeProps.primaryColor,
|
||||
),
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: _pageTransitionsTheme,
|
||||
colorScheme: _getAppColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: themeProps.primaryColor,
|
||||
).toPureBlack(themeProps.pureBlack),
|
||||
),
|
||||
home: child,
|
||||
);
|
||||
},
|
||||
child: const HomePage(),
|
||||
|
||||
@@ -58,6 +58,8 @@ mixin ClashInterface {
|
||||
|
||||
stopLog();
|
||||
|
||||
Future<bool> crash();
|
||||
|
||||
FutureOr<String> getConnections();
|
||||
|
||||
FutureOr<bool> closeConnection(String id);
|
||||
@@ -104,6 +106,7 @@ abstract class ClashHandlerInterface with ClashInterface {
|
||||
case ActionMethod.closeConnection:
|
||||
case ActionMethod.stopListener:
|
||||
case ActionMethod.setState:
|
||||
case ActionMethod.crash:
|
||||
completer?.complete(result.data as bool);
|
||||
return;
|
||||
case ActionMethod.changeProxy:
|
||||
@@ -242,6 +245,14 @@ abstract class ClashHandlerInterface with ClashInterface {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Future<bool> crash() {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.crash,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getProxies() {
|
||||
return invoke<String>(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:fl_clash/manager/manager.dart';
|
||||
import 'package:fl_clash/manager/message_manager.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -11,6 +11,36 @@ extension BuildContextExtension on BuildContext {
|
||||
return findAncestorStateOfType<MessageManagerState>()?.message(text);
|
||||
}
|
||||
|
||||
showSnackBar(
|
||||
String message, {
|
||||
SnackBarAction? action,
|
||||
}) {
|
||||
final width = viewWidth;
|
||||
EdgeInsets margin;
|
||||
if (width < 600) {
|
||||
margin = const EdgeInsets.only(
|
||||
bottom: 16,
|
||||
right: 16,
|
||||
left: 16,
|
||||
);
|
||||
} else {
|
||||
margin = EdgeInsets.only(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: width - 316,
|
||||
);
|
||||
}
|
||||
ScaffoldMessenger.of(this).showSnackBar(
|
||||
SnackBar(
|
||||
action: action,
|
||||
content: Text(message),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
duration: const Duration(milliseconds: 1500),
|
||||
margin: margin,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Size get appSize {
|
||||
return MediaQuery.of(this).size;
|
||||
}
|
||||
@@ -27,10 +57,10 @@ extension BuildContextExtension on BuildContext {
|
||||
T? state;
|
||||
|
||||
visitor(Element element) {
|
||||
if(!element.mounted){
|
||||
if (!element.mounted) {
|
||||
return;
|
||||
}
|
||||
if(element is StatefulElement){
|
||||
if (element is StatefulElement) {
|
||||
if (element.state is T) {
|
||||
state = element.state as T;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class CommonPrint {
|
||||
}
|
||||
globalState.appController.addLog(
|
||||
Log(
|
||||
logLevel: LogLevel.info,
|
||||
logLevel: LogLevel.app,
|
||||
payload: payload,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ class Tray {
|
||||
);
|
||||
}
|
||||
menuItems.add(MenuItem.separator());
|
||||
if (!Platform.isWindows) {
|
||||
if (Platform.isMacOS) {
|
||||
for (final group in trayState.groups) {
|
||||
List<MenuItem> subMenuItems = [];
|
||||
for (final proxy in group.all) {
|
||||
|
||||
@@ -345,12 +345,16 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> updateGroups() async {
|
||||
_ref.read(groupsProvider.notifier).value = await retry(
|
||||
task: () async {
|
||||
return await clashCore.getProxiesGroups();
|
||||
},
|
||||
retryIf: (res) => res.isEmpty,
|
||||
);
|
||||
try {
|
||||
_ref.read(groupsProvider.notifier).value = await retry(
|
||||
task: () async {
|
||||
return await clashCore.getProxiesGroups();
|
||||
},
|
||||
retryIf: (res) => res.isEmpty,
|
||||
);
|
||||
} catch (_) {
|
||||
_ref.read(groupsProvider.notifier).value = [];
|
||||
}
|
||||
}
|
||||
|
||||
updateProfiles() async {
|
||||
@@ -362,10 +366,6 @@ class AppController {
|
||||
}
|
||||
}
|
||||
|
||||
updateSystemColorSchemes(ColorSchemes colorSchemes) {
|
||||
_ref.read(appSchemesProvider.notifier).value = colorSchemes;
|
||||
}
|
||||
|
||||
savePreferences() async {
|
||||
commonPrint.log("save preferences");
|
||||
await preferences.saveConfig(globalState.config);
|
||||
@@ -410,6 +410,14 @@ class AppController {
|
||||
}
|
||||
}
|
||||
|
||||
Future handleClear() async {
|
||||
await preferences.clearPreferences();
|
||||
commonPrint.log("clear preferences");
|
||||
globalState.config = Config(
|
||||
themeProps: defaultThemeProps,
|
||||
);
|
||||
}
|
||||
|
||||
autoCheckUpdate() async {
|
||||
if (!_ref.read(appSettingProvider).autoCheckUpdate) return;
|
||||
final res = await request.checkForUpdate();
|
||||
|
||||
@@ -91,7 +91,14 @@ enum Mode { rule, global, direct }
|
||||
|
||||
enum ViewMode { mobile, laptop, desktop }
|
||||
|
||||
enum LogLevel { debug, info, warning, error, silent }
|
||||
enum LogLevel {
|
||||
debug,
|
||||
info,
|
||||
warning,
|
||||
error,
|
||||
silent,
|
||||
app,
|
||||
}
|
||||
|
||||
enum TransportProtocol { udp, tcp }
|
||||
|
||||
@@ -262,6 +269,7 @@ enum ActionMethod {
|
||||
getCountryCode,
|
||||
getMemory,
|
||||
getProfile,
|
||||
crash,
|
||||
|
||||
///Android,
|
||||
setFdMap,
|
||||
@@ -308,6 +316,12 @@ enum DashboardWidget {
|
||||
child: NetworkSpeed(),
|
||||
),
|
||||
),
|
||||
outboundModeV2(
|
||||
GridItem(
|
||||
crossAxisCellCount: 8,
|
||||
child: OutboundModeV2(),
|
||||
),
|
||||
),
|
||||
outboundMode(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/list.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
@immutable
|
||||
class Contributor {
|
||||
@@ -116,33 +120,43 @@ class AboutFragment extends StatelessWidget {
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: 16,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Image.asset(
|
||||
'assets/images/icon.png',
|
||||
width: 64,
|
||||
height: 64,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
Consumer(builder: (_, ref, ___) {
|
||||
return _DeveloperModeDetector(
|
||||
child: Wrap(
|
||||
spacing: 16,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
appName,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Image.asset(
|
||||
'assets/images/icon.png',
|
||||
width: 64,
|
||||
height: 64,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
globalState.packageInfo.version,
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
appName,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
Text(
|
||||
globalState.packageInfo.version,
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
onEnterDeveloperMode: () {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(developerMode: true),
|
||||
);
|
||||
context.showNotifier(appLocalizations.developerModeEnableTip);
|
||||
},
|
||||
);
|
||||
}),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
@@ -209,3 +223,52 @@ class Avatar extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeveloperModeDetector extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback onEnterDeveloperMode;
|
||||
|
||||
const _DeveloperModeDetector({
|
||||
required this.child,
|
||||
required this.onEnterDeveloperMode,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_DeveloperModeDetector> createState() => _DeveloperModeDetectorState();
|
||||
}
|
||||
|
||||
class _DeveloperModeDetectorState extends State<_DeveloperModeDetector> {
|
||||
int _counter = 0;
|
||||
Timer? _timer;
|
||||
|
||||
void _handleTap() {
|
||||
_counter++;
|
||||
if (_counter >= 5) {
|
||||
widget.onEnterDeveloperMode();
|
||||
_resetCounter();
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
_timer = Timer(Duration(seconds: 1), _resetCounter);
|
||||
}
|
||||
}
|
||||
|
||||
void _resetCounter() {
|
||||
_counter = 0;
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: _handleTap,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ class _DashboardFragmentState extends ConsumerState<DashboardFragment>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dashboardState = ref.watch(dashboardStateProvider);
|
||||
final columns = max(4 * ((dashboardState.viewWidth / 350).ceil()), 8);
|
||||
final columns = max(4 * ((dashboardState.viewWidth / 320).ceil()), 8);
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SingleChildScrollView(
|
||||
|
||||
@@ -72,3 +72,63 @@ class OutboundMode extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OutboundModeV2 extends StatelessWidget {
|
||||
const OutboundModeV2({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final height = getWidgetHeight(0.72);
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: CommonCard(
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final mode = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.mode,
|
||||
),
|
||||
);
|
||||
final thumbColor = switch (mode) {
|
||||
Mode.rule => context.colorScheme.secondaryContainer,
|
||||
Mode.global => globalState.theme.darken3PrimaryContainer,
|
||||
Mode.direct => context.colorScheme.tertiaryContainer,
|
||||
};
|
||||
return Container(
|
||||
constraints: BoxConstraints.expand(),
|
||||
child: CommonTabBar<Mode>(
|
||||
children: Map.fromEntries(
|
||||
Mode.values.map(
|
||||
(item) => MapEntry(
|
||||
item,
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
height: height - 16,
|
||||
child: Text(
|
||||
Intl.message(item.name),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall
|
||||
?.adjustSize(1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
groupValue: mode,
|
||||
onValueChanged: (value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.changeMode(value);
|
||||
},
|
||||
thumbColor: thumbColor,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
86
lib/fragments/developer.dart
Normal file
86
lib/fragments/developer.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'package:fl_clash/clash/core.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class DeveloperView extends ConsumerWidget {
|
||||
const DeveloperView({super.key});
|
||||
|
||||
Widget _getDeveloperList(BuildContext context) {
|
||||
return generateSectionV2(
|
||||
title: appLocalizations.options,
|
||||
items: [
|
||||
ListItem(
|
||||
leading: Icon(Icons.ac_unit),
|
||||
title: Text(appLocalizations.messageTest),
|
||||
onTap: () {
|
||||
context.showNotifier(
|
||||
appLocalizations.messageTestTip,
|
||||
);
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
leading: Icon(Icons.heart_broken),
|
||||
title: Text(appLocalizations.crashTest),
|
||||
onTap: () {
|
||||
clashCore.clashInterface.crash();
|
||||
},
|
||||
),
|
||||
ListItem(
|
||||
leading: Icon(Icons.delete_forever),
|
||||
title: Text(appLocalizations.clearData),
|
||||
onTap: () async {
|
||||
await globalState.appController.handleClear();
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final enable = ref.watch(
|
||||
appSettingProvider.select(
|
||||
(state) => state.developerMode,
|
||||
),
|
||||
);
|
||||
return SingleChildScrollView(
|
||||
padding: baseInfoEdgeInsets,
|
||||
child: Column(
|
||||
children: [
|
||||
CommonCard(
|
||||
type: CommonCardType.filled,
|
||||
radius: 18,
|
||||
child: ListItem.switchItem(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
),
|
||||
title: Text(appLocalizations.developerMode),
|
||||
delegate: SwitchDelegate(
|
||||
value: enable,
|
||||
onChanged: (value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
developerMode: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
_getDeveloperList(context)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,3 +11,4 @@ export 'backup_and_recovery.dart';
|
||||
export 'resources.dart';
|
||||
export 'connection/requests.dart';
|
||||
export 'connection/connections.dart';
|
||||
export 'developer.dart';
|
||||
|
||||
@@ -104,8 +104,13 @@ class _URLFormDialogState extends State<URLFormDialog> {
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
TextField(
|
||||
maxLines: 5,
|
||||
keyboardType: TextInputType.url,
|
||||
minLines: 1,
|
||||
maxLines: 5,
|
||||
onSubmitted: (_) {
|
||||
_handleAddProfileFormURL();
|
||||
},
|
||||
onEditingComplete: _handleAddProfileFormURL,
|
||||
controller: urlController,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
|
||||
@@ -214,6 +214,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
final items = [
|
||||
ListItem(
|
||||
title: TextFormField(
|
||||
textInputAction: TextInputAction.next,
|
||||
controller: labelController,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
@@ -230,6 +231,8 @@ class _EditProfileState extends State<EditProfile> {
|
||||
if (widget.profile.type == ProfileType.url) ...[
|
||||
ListItem(
|
||||
title: TextFormField(
|
||||
textInputAction: TextInputAction.next,
|
||||
keyboardType: TextInputType.url,
|
||||
controller: urlController,
|
||||
maxLines: 5,
|
||||
minLines: 1,
|
||||
@@ -258,6 +261,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
if (autoUpdate)
|
||||
ListItem(
|
||||
title: TextFormField(
|
||||
textInputAction: TextInputAction.next,
|
||||
controller: autoUpdateDurationController,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
|
||||
@@ -873,6 +873,8 @@ class _AddRuleDialogState extends State<AddRuleDialog> {
|
||||
builder: (filed) {
|
||||
return DropdownMenu(
|
||||
width: 200,
|
||||
enableFilter: false,
|
||||
enableSearch: false,
|
||||
controller: _subRuleController,
|
||||
label: Text(appLocalizations.subRule),
|
||||
menuHeight: 250,
|
||||
@@ -890,11 +892,11 @@ class _AddRuleDialogState extends State<AddRuleDialog> {
|
||||
builder: (filed) {
|
||||
return DropdownMenu(
|
||||
controller: _ruleTargetController,
|
||||
initialSelection: filed.value,
|
||||
label: Text(appLocalizations.ruleTarget),
|
||||
width: 200,
|
||||
menuHeight: 250,
|
||||
enableFilter: true,
|
||||
enableFilter: false,
|
||||
enableSearch: false,
|
||||
dropdownMenuEntries: _targetItems,
|
||||
errorText: filed.errorText,
|
||||
);
|
||||
|
||||
@@ -309,124 +309,138 @@ class _PrimaryColorItemState extends ConsumerState<_PrimaryColorItem> {
|
||||
final primaryColors = [null, ...vm3.b];
|
||||
final schemeVariant = vm3.c;
|
||||
|
||||
return ItemCard(
|
||||
info: Info(
|
||||
label: appLocalizations.themeColor,
|
||||
iconData: Icons.palette,
|
||||
),
|
||||
actions: genActions(
|
||||
[
|
||||
if (_removablePrimaryColor == null)
|
||||
FilledButton(
|
||||
style: ButtonStyle(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: _handleChangeSchemeVariant,
|
||||
child: Text(Intl.message("${schemeVariant.name}Scheme")),
|
||||
),
|
||||
_removablePrimaryColor != null
|
||||
? FilledButton(
|
||||
style: ButtonStyle(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_removablePrimaryColor = null;
|
||||
});
|
||||
},
|
||||
child: Text(appLocalizations.cancel),
|
||||
)
|
||||
: IconButton.filledTonal(
|
||||
iconSize: 20,
|
||||
padding: EdgeInsets.all(4),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: _handleReset,
|
||||
icon: Icon(Icons.replay),
|
||||
)
|
||||
],
|
||||
space: 8,
|
||||
),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
return CommonPopScope(
|
||||
onPop: () {
|
||||
if (_removablePrimaryColor != null) {
|
||||
setState(() {
|
||||
_removablePrimaryColor = null;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
child: ItemCard(
|
||||
info: Info(
|
||||
label: appLocalizations.themeColor,
|
||||
iconData: Icons.palette,
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
final columns = _calcColumns(constraints.maxWidth);
|
||||
final itemWidth =
|
||||
(constraints.maxWidth - (columns - 1) * 16) / columns;
|
||||
return Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
for (final color in primaryColors)
|
||||
Container(
|
||||
clipBehavior: Clip.none,
|
||||
width: itemWidth,
|
||||
height: itemWidth,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
actions: genActions(
|
||||
[
|
||||
if (_removablePrimaryColor == null)
|
||||
FilledButton(
|
||||
style: ButtonStyle(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: _handleChangeSchemeVariant,
|
||||
child: Text(Intl.message("${schemeVariant.name}Scheme")),
|
||||
),
|
||||
_removablePrimaryColor != null
|
||||
? FilledButton(
|
||||
style: ButtonStyle(
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_removablePrimaryColor = null;
|
||||
});
|
||||
},
|
||||
child: Text(appLocalizations.cancel),
|
||||
)
|
||||
: IconButton.filledTonal(
|
||||
iconSize: 20,
|
||||
padding: EdgeInsets.all(4),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: _handleReset,
|
||||
icon: Icon(Icons.replay),
|
||||
)
|
||||
],
|
||||
space: 8,
|
||||
),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
final columns = _calcColumns(constraints.maxWidth);
|
||||
final itemWidth =
|
||||
(constraints.maxWidth - (columns - 1) * 16) / columns;
|
||||
return Wrap(
|
||||
spacing: 16,
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
for (final color in primaryColors)
|
||||
Container(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
EffectGestureDetector(
|
||||
child: ColorSchemeBox(
|
||||
isSelected: color == primaryColor,
|
||||
primaryColor: color != null ? Color(color) : null,
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(themeSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
primaryColor: color,
|
||||
),
|
||||
);
|
||||
width: itemWidth,
|
||||
height: itemWidth,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
EffectGestureDetector(
|
||||
child: ColorSchemeBox(
|
||||
isSelected: color == primaryColor,
|
||||
primaryColor: color != null ? Color(color) : null,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_removablePrimaryColor = null;
|
||||
});
|
||||
ref
|
||||
.read(themeSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
primaryColor: color,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
onLongPress: () {
|
||||
setState(() {
|
||||
_removablePrimaryColor = color;
|
||||
});
|
||||
},
|
||||
),
|
||||
onLongPress: () {
|
||||
setState(() {
|
||||
_removablePrimaryColor = color;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (_removablePrimaryColor != null &&
|
||||
_removablePrimaryColor == color)
|
||||
Container(
|
||||
color: Colors.white.opacity0,
|
||||
padding: EdgeInsets.all(8),
|
||||
child: IconButton.filledTonal(
|
||||
onPressed: _handleDel,
|
||||
padding: EdgeInsets.all(12),
|
||||
iconSize: 30,
|
||||
icon: Icon(
|
||||
color: context.colorScheme.primary,
|
||||
Icons.delete,
|
||||
if (_removablePrimaryColor != null &&
|
||||
_removablePrimaryColor == color)
|
||||
Container(
|
||||
color: Colors.white.opacity0,
|
||||
padding: EdgeInsets.all(8),
|
||||
child: IconButton.filledTonal(
|
||||
onPressed: _handleDel,
|
||||
padding: EdgeInsets.all(12),
|
||||
iconSize: 30,
|
||||
icon: Icon(
|
||||
color: context.colorScheme.primary,
|
||||
Icons.delete,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (_removablePrimaryColor == null)
|
||||
Container(
|
||||
width: itemWidth,
|
||||
height: itemWidth,
|
||||
padding: EdgeInsets.all(
|
||||
4,
|
||||
),
|
||||
child: IconButton.filledTonal(
|
||||
onPressed: _handleAdd,
|
||||
iconSize: 32,
|
||||
icon: Icon(
|
||||
color: context.colorScheme.primary,
|
||||
Icons.add,
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
if (_removablePrimaryColor == null)
|
||||
Container(
|
||||
width: itemWidth,
|
||||
height: itemWidth,
|
||||
padding: EdgeInsets.all(
|
||||
4,
|
||||
),
|
||||
child: IconButton.filledTonal(
|
||||
onPressed: _handleAdd,
|
||||
iconSize: 32,
|
||||
icon: Icon(
|
||||
color: context.colorScheme.primary,
|
||||
Icons.add,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'backup_and_recovery.dart';
|
||||
import 'developer.dart';
|
||||
import 'theme.dart';
|
||||
import 'package:path/path.dart' show dirname, join;
|
||||
|
||||
@@ -54,11 +55,12 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _getOtherList() {
|
||||
List<Widget> _getOtherList(bool enableDeveloperMode) {
|
||||
return generateSection(
|
||||
title: appLocalizations.other,
|
||||
items: [
|
||||
_DisclaimerItem(),
|
||||
if (enableDeveloperMode) _DeveloperItem(),
|
||||
_InfoItem(),
|
||||
],
|
||||
);
|
||||
@@ -82,7 +84,11 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ref.watch(appSettingProvider.select((state) => state.locale));
|
||||
final vm2 = ref.watch(
|
||||
appSettingProvider.select(
|
||||
(state) => VM2(a: state.locale, b: state.developerMode),
|
||||
),
|
||||
);
|
||||
final items = [
|
||||
Consumer(
|
||||
builder: (_, ref, __) {
|
||||
@@ -99,7 +105,7 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
|
||||
},
|
||||
),
|
||||
..._getSettingList(),
|
||||
..._getOtherList(),
|
||||
..._getOtherList(vm2.b),
|
||||
];
|
||||
return ListView.builder(
|
||||
itemCount: items.length,
|
||||
@@ -297,3 +303,19 @@ class _InfoItem extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeveloperItem extends StatelessWidget {
|
||||
const _DeveloperItem();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListItem.open(
|
||||
leading: const Icon(Icons.developer_mode),
|
||||
title: Text(appLocalizations.developerMode),
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.developerMode,
|
||||
widget: const DeveloperView(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,5 +385,11 @@
|
||||
"expressiveScheme": "Expressive",
|
||||
"contentScheme": "Content",
|
||||
"rainbowScheme": "Rainbow",
|
||||
"fruitSaladScheme": "FruitSalad"
|
||||
"fruitSaladScheme": "FruitSalad",
|
||||
"developerMode": "Developer mode",
|
||||
"developerModeEnableTip": "Developer mode is enabled.",
|
||||
"messageTest": "Message test",
|
||||
"messageTestTip": "This is a message.",
|
||||
"crashTest": "Crash test",
|
||||
"clearData": "Clear Data"
|
||||
}
|
||||
@@ -385,5 +385,11 @@
|
||||
"expressiveScheme": "エクスプレッシブ",
|
||||
"contentScheme": "コンテンツテーマ",
|
||||
"rainbowScheme": "レインボー",
|
||||
"fruitSaladScheme": "フルーツサラダ"
|
||||
"fruitSaladScheme": "フルーツサラダ",
|
||||
"developerMode": "デベロッパーモード",
|
||||
"developerModeEnableTip": "デベロッパーモードが有効になりました。",
|
||||
"messageTest": "メッセージテスト",
|
||||
"messageTestTip": "これはメッセージです。",
|
||||
"crashTest": "クラッシュテスト",
|
||||
"clearData": "データを消去"
|
||||
}
|
||||
@@ -385,5 +385,11 @@
|
||||
"expressiveScheme": "Экспрессивные",
|
||||
"contentScheme": "Контентная тема",
|
||||
"rainbowScheme": "Радужные",
|
||||
"fruitSaladScheme": "Фруктовый микс"
|
||||
"fruitSaladScheme": "Фруктовый микс",
|
||||
"developerMode": "Режим разработчика",
|
||||
"developerModeEnableTip": "Режим разработчика активирован.",
|
||||
"messageTest": "Тестирование сообщения",
|
||||
"messageTestTip": "Это сообщение.",
|
||||
"crashTest": "Тест на сбои",
|
||||
"clearData": "Очистить данные"
|
||||
}
|
||||
@@ -385,5 +385,11 @@
|
||||
"expressiveScheme": "表现力",
|
||||
"contentScheme": "内容主题",
|
||||
"rainbowScheme": "彩虹",
|
||||
"fruitSaladScheme": "果缤纷"
|
||||
"fruitSaladScheme": "果缤纷",
|
||||
"developerMode": "开发者模式",
|
||||
"developerModeEnableTip": "开发者模式已启用。",
|
||||
"messageTest": "消息测试",
|
||||
"messageTestTip": "这是一条消息。",
|
||||
"crashTest": "崩溃测试",
|
||||
"clearData": "清除数据"
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"The current application is already the latest version",
|
||||
),
|
||||
"checking": MessageLookupByLibrary.simpleMessage("Checking..."),
|
||||
"clearData": MessageLookupByLibrary.simpleMessage("Clear Data"),
|
||||
"clipboardExport": MessageLookupByLibrary.simpleMessage("Export clipboard"),
|
||||
"clipboardImport": MessageLookupByLibrary.simpleMessage("Clipboard import"),
|
||||
"colorExists": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -177,6 +178,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"core": MessageLookupByLibrary.simpleMessage("Core"),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("Country"),
|
||||
"crashTest": MessageLookupByLibrary.simpleMessage("Crash test"),
|
||||
"create": MessageLookupByLibrary.simpleMessage("Create"),
|
||||
"cut": MessageLookupByLibrary.simpleMessage("Cut"),
|
||||
"dark": MessageLookupByLibrary.simpleMessage("Dark"),
|
||||
@@ -208,6 +210,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"detectionTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Relying on third-party api is for reference only",
|
||||
),
|
||||
"developerMode": MessageLookupByLibrary.simpleMessage("Developer mode"),
|
||||
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Developer mode is enabled.",
|
||||
),
|
||||
"direct": MessageLookupByLibrary.simpleMessage("Direct"),
|
||||
"disclaimer": MessageLookupByLibrary.simpleMessage("Disclaimer"),
|
||||
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -362,6 +368,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"loose": MessageLookupByLibrary.simpleMessage("Loose"),
|
||||
"memoryInfo": MessageLookupByLibrary.simpleMessage("Memory info"),
|
||||
"messageTest": MessageLookupByLibrary.simpleMessage("Message test"),
|
||||
"messageTestTip": MessageLookupByLibrary.simpleMessage(
|
||||
"This is a message.",
|
||||
),
|
||||
"min": MessageLookupByLibrary.simpleMessage("Min"),
|
||||
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("Minimize on exit"),
|
||||
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -104,6 +104,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"checkUpdate": MessageLookupByLibrary.simpleMessage("更新を確認"),
|
||||
"checkUpdateError": MessageLookupByLibrary.simpleMessage("アプリは最新版です"),
|
||||
"checking": MessageLookupByLibrary.simpleMessage("確認中..."),
|
||||
"clearData": MessageLookupByLibrary.simpleMessage("データを消去"),
|
||||
"clipboardExport": MessageLookupByLibrary.simpleMessage("クリップボードにエクスポート"),
|
||||
"clipboardImport": MessageLookupByLibrary.simpleMessage("クリップボードからインポート"),
|
||||
"colorExists": MessageLookupByLibrary.simpleMessage("この色は既に存在します"),
|
||||
@@ -127,6 +128,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"core": MessageLookupByLibrary.simpleMessage("コア"),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("コア情報"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("国"),
|
||||
"crashTest": MessageLookupByLibrary.simpleMessage("クラッシュテスト"),
|
||||
"create": MessageLookupByLibrary.simpleMessage("作成"),
|
||||
"cut": MessageLookupByLibrary.simpleMessage("切り取り"),
|
||||
"dark": MessageLookupByLibrary.simpleMessage("ダーク"),
|
||||
@@ -150,6 +152,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
|
||||
),
|
||||
"detectionTip": MessageLookupByLibrary.simpleMessage("サードパーティAPIに依存(参考値)"),
|
||||
"developerMode": MessageLookupByLibrary.simpleMessage("デベロッパーモード"),
|
||||
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage(
|
||||
"デベロッパーモードが有効になりました。",
|
||||
),
|
||||
"direct": MessageLookupByLibrary.simpleMessage("ダイレクト"),
|
||||
"disclaimer": MessageLookupByLibrary.simpleMessage("免責事項"),
|
||||
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -258,6 +264,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"loopbackDesc": MessageLookupByLibrary.simpleMessage("UWPループバック解除用"),
|
||||
"loose": MessageLookupByLibrary.simpleMessage("疎"),
|
||||
"memoryInfo": MessageLookupByLibrary.simpleMessage("メモリ情報"),
|
||||
"messageTest": MessageLookupByLibrary.simpleMessage("メッセージテスト"),
|
||||
"messageTestTip": MessageLookupByLibrary.simpleMessage("これはメッセージです。"),
|
||||
"min": MessageLookupByLibrary.simpleMessage("最小化"),
|
||||
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("終了時に最小化"),
|
||||
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -148,6 +148,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Текущее приложение уже является последней версией",
|
||||
),
|
||||
"checking": MessageLookupByLibrary.simpleMessage("Проверка..."),
|
||||
"clearData": MessageLookupByLibrary.simpleMessage("Очистить данные"),
|
||||
"clipboardExport": MessageLookupByLibrary.simpleMessage(
|
||||
"Экспорт в буфер обмена",
|
||||
),
|
||||
@@ -183,6 +184,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"core": MessageLookupByLibrary.simpleMessage("Ядро"),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("Информация о ядре"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("Страна"),
|
||||
"crashTest": MessageLookupByLibrary.simpleMessage("Тест на сбои"),
|
||||
"create": MessageLookupByLibrary.simpleMessage("Создать"),
|
||||
"cut": MessageLookupByLibrary.simpleMessage("Вырезать"),
|
||||
"dark": MessageLookupByLibrary.simpleMessage("Темный"),
|
||||
@@ -216,6 +218,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"detectionTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Опирается на сторонний API, только для справки",
|
||||
),
|
||||
"developerMode": MessageLookupByLibrary.simpleMessage("Режим разработчика"),
|
||||
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Режим разработчика активирован.",
|
||||
),
|
||||
"direct": MessageLookupByLibrary.simpleMessage("Прямой"),
|
||||
"disclaimer": MessageLookupByLibrary.simpleMessage(
|
||||
"Отказ от ответственности",
|
||||
@@ -386,6 +392,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"loose": MessageLookupByLibrary.simpleMessage("Свободный"),
|
||||
"memoryInfo": MessageLookupByLibrary.simpleMessage("Информация о памяти"),
|
||||
"messageTest": MessageLookupByLibrary.simpleMessage(
|
||||
"Тестирование сообщения",
|
||||
),
|
||||
"messageTestTip": MessageLookupByLibrary.simpleMessage("Это сообщение."),
|
||||
"min": MessageLookupByLibrary.simpleMessage("Мин"),
|
||||
"minimizeOnExit": MessageLookupByLibrary.simpleMessage(
|
||||
"Свернуть при выходе",
|
||||
|
||||
@@ -94,6 +94,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"),
|
||||
"checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"),
|
||||
"checking": MessageLookupByLibrary.simpleMessage("检测中..."),
|
||||
"clearData": MessageLookupByLibrary.simpleMessage("清除数据"),
|
||||
"clipboardExport": MessageLookupByLibrary.simpleMessage("导出剪贴板"),
|
||||
"clipboardImport": MessageLookupByLibrary.simpleMessage("剪贴板导入"),
|
||||
"colorExists": MessageLookupByLibrary.simpleMessage("该颜色已存在"),
|
||||
@@ -117,6 +118,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"core": MessageLookupByLibrary.simpleMessage("内核"),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("区域"),
|
||||
"crashTest": MessageLookupByLibrary.simpleMessage("崩溃测试"),
|
||||
"create": MessageLookupByLibrary.simpleMessage("创建"),
|
||||
"cut": MessageLookupByLibrary.simpleMessage("剪切"),
|
||||
"dark": MessageLookupByLibrary.simpleMessage("深色"),
|
||||
@@ -136,6 +138,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"基于ClashMeta的多平台代理客户端,简单易用,开源无广告。",
|
||||
),
|
||||
"detectionTip": MessageLookupByLibrary.simpleMessage("依赖第三方api,仅供参考"),
|
||||
"developerMode": MessageLookupByLibrary.simpleMessage("开发者模式"),
|
||||
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage("开发者模式已启用。"),
|
||||
"direct": MessageLookupByLibrary.simpleMessage("直连"),
|
||||
"disclaimer": MessageLookupByLibrary.simpleMessage("免责声明"),
|
||||
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -232,6 +236,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"loopbackDesc": MessageLookupByLibrary.simpleMessage("用于UWP回环解锁"),
|
||||
"loose": MessageLookupByLibrary.simpleMessage("宽松"),
|
||||
"memoryInfo": MessageLookupByLibrary.simpleMessage("内存信息"),
|
||||
"messageTest": MessageLookupByLibrary.simpleMessage("消息测试"),
|
||||
"messageTestTip": MessageLookupByLibrary.simpleMessage("这是一条消息。"),
|
||||
"min": MessageLookupByLibrary.simpleMessage("最小"),
|
||||
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"),
|
||||
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage("修改系统默认退出事件"),
|
||||
|
||||
@@ -3004,6 +3004,56 @@ class AppLocalizations {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Developer mode`
|
||||
String get developerMode {
|
||||
return Intl.message(
|
||||
'Developer mode',
|
||||
name: 'developerMode',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Developer mode is enabled.`
|
||||
String get developerModeEnableTip {
|
||||
return Intl.message(
|
||||
'Developer mode is enabled.',
|
||||
name: 'developerModeEnableTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Message test`
|
||||
String get messageTest {
|
||||
return Intl.message(
|
||||
'Message test',
|
||||
name: 'messageTest',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `This is a message.`
|
||||
String get messageTestTip {
|
||||
return Intl.message(
|
||||
'This is a message.',
|
||||
name: 'messageTestTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Crash test`
|
||||
String get crashTest {
|
||||
return Intl.message('Crash test', name: 'crashTest', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Clear Data`
|
||||
String get clearData {
|
||||
return Intl.message('Clear Data', name: 'clearData', desc: '', args: []);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -21,7 +21,7 @@ class MessageManager extends StatefulWidget {
|
||||
class MessageManagerState extends State<MessageManager> {
|
||||
final _messagesNotifier = ValueNotifier<List<CommonMessage>>([]);
|
||||
final List<CommonMessage> _bufferMessages = [];
|
||||
Completer<bool>? _messageIngCompleter;
|
||||
bool _pushing = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -40,26 +40,27 @@ class MessageManagerState extends State<MessageManager> {
|
||||
text: text,
|
||||
);
|
||||
_bufferMessages.add(commonMessage);
|
||||
_showMessage();
|
||||
await _showMessage();
|
||||
}
|
||||
|
||||
_showMessage() async {
|
||||
if (_messageIngCompleter?.isCompleted == false) {
|
||||
if (_pushing == true) {
|
||||
return;
|
||||
}
|
||||
_pushing = true;
|
||||
while (_bufferMessages.isNotEmpty) {
|
||||
final commonMessage = _bufferMessages.removeAt(0);
|
||||
_messagesNotifier.value = List.from(_messagesNotifier.value)
|
||||
..add(
|
||||
commonMessage,
|
||||
);
|
||||
|
||||
_messageIngCompleter = Completer();
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
Future.delayed(commonMessage.duration, () {
|
||||
_handleRemove(commonMessage);
|
||||
});
|
||||
_messageIngCompleter?.complete(true);
|
||||
if (_bufferMessages.isEmpty) {
|
||||
_pushing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,41 +78,41 @@ class MessageManagerState extends State<MessageManager> {
|
||||
valueListenable: _messagesNotifier,
|
||||
builder: (_, messages, __) {
|
||||
return FadeThroughBox(
|
||||
margin: EdgeInsets.only(
|
||||
top: kToolbarHeight + 8,
|
||||
left: 12,
|
||||
right: 12,
|
||||
),
|
||||
alignment: Alignment.topRight,
|
||||
child: messages.isEmpty
|
||||
? SizedBox()
|
||||
: LayoutBuilder(
|
||||
key: Key(messages.last.id),
|
||||
builder: (_, constraints) {
|
||||
return Card(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(12.0),
|
||||
),
|
||||
),
|
||||
elevation: 10,
|
||||
margin: EdgeInsets.only(
|
||||
top: kToolbarHeight + 8,
|
||||
left: 12,
|
||||
right: 12,
|
||||
),
|
||||
color: context.colorScheme.surfaceContainerHigh,
|
||||
child: Container(
|
||||
width: min(
|
||||
constraints.maxWidth,
|
||||
400,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Text(
|
||||
messages.last.text,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
key: Key(messages.last.id),
|
||||
builder: (_, constraints) {
|
||||
return Card(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(12.0),
|
||||
),
|
||||
),
|
||||
elevation: 10,
|
||||
color: context.colorScheme.surfaceContainerHigh,
|
||||
child: Container(
|
||||
width: min(
|
||||
constraints.maxWidth,
|
||||
500,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Text(
|
||||
messages.last.text,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -16,11 +16,16 @@ class ThemeManager extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
globalState.measure = Measure.of(context);
|
||||
globalState.theme = CommonTheme.of(context);
|
||||
final padding = MediaQuery.of(context).padding;
|
||||
final height = MediaQuery.of(context).size.height;
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaler: TextScaler.linear(
|
||||
textScaleFactor,
|
||||
),
|
||||
padding: padding.copyWith(
|
||||
top: padding.top > height * 0.3 ? 0.0 : padding.top,
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
|
||||
@@ -16,7 +16,6 @@ class AppState with _$AppState {
|
||||
@Default(false) bool isInit,
|
||||
@Default(PageLabel.dashboard) PageLabel pageLabel,
|
||||
@Default([]) List<Package> packages,
|
||||
@Default(ColorSchemes()) ColorSchemes colorSchemes,
|
||||
@Default(0) int sortNum,
|
||||
required Size viewSize,
|
||||
@Default({}) DelayMap delayMap,
|
||||
|
||||
@@ -37,7 +37,9 @@ const defaultNetworkProps = NetworkProps();
|
||||
const defaultProxiesStyle = ProxiesStyle();
|
||||
const defaultWindowProps = WindowProps();
|
||||
const defaultAccessControl = AccessControl();
|
||||
const defaultThemeProps = ThemeProps();
|
||||
final defaultThemeProps = ThemeProps(
|
||||
primaryColor: defaultPrimaryColor,
|
||||
);
|
||||
|
||||
const List<DashboardWidget> defaultDashboardWidgets = [
|
||||
DashboardWidget.networkSpeed,
|
||||
@@ -82,6 +84,7 @@ class AppSettingProps with _$AppSettingProps {
|
||||
@Default(false) bool disclaimerAccepted,
|
||||
@Default(true) bool minimizeOnExit,
|
||||
@Default(false) bool hidden,
|
||||
@Default(false) bool developerMode,
|
||||
}) = _AppSettingProps;
|
||||
|
||||
factory AppSettingProps.fromJson(Map<String, Object?> json) =>
|
||||
@@ -173,15 +176,26 @@ class ProxiesStyle with _$ProxiesStyle {
|
||||
@freezed
|
||||
class ThemeProps with _$ThemeProps {
|
||||
const factory ThemeProps({
|
||||
@Default(defaultPrimaryColor) int? primaryColor,
|
||||
int? primaryColor,
|
||||
@Default(defaultPrimaryColors) List<int> primaryColors,
|
||||
@Default(ThemeMode.dark) ThemeMode themeMode,
|
||||
@Default(DynamicSchemeVariant.tonalSpot) DynamicSchemeVariant schemeVariant,
|
||||
@Default(false) bool pureBlack,
|
||||
}) = _ThemeProps;
|
||||
|
||||
factory ThemeProps.fromJson(Map<String, Object?>? json) =>
|
||||
json == null ? defaultThemeProps : _$ThemePropsFromJson(json);
|
||||
factory ThemeProps.fromJson(Map<String, Object?> json) =>
|
||||
_$ThemePropsFromJson(json);
|
||||
|
||||
factory ThemeProps.safeFromJson(Map<String, Object?>? json) {
|
||||
if (json == null) {
|
||||
return defaultThemeProps;
|
||||
}
|
||||
try {
|
||||
return ThemeProps.fromJson(json);
|
||||
} catch (_) {
|
||||
return defaultThemeProps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
@@ -197,7 +211,7 @@ class Config with _$Config {
|
||||
DAV? dav,
|
||||
@Default(defaultNetworkProps) NetworkProps networkProps,
|
||||
@Default(defaultVpnProps) VpnProps vpnProps,
|
||||
@Default(defaultThemeProps) ThemeProps themeProps,
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson) required ThemeProps themeProps,
|
||||
@Default(defaultProxiesStyle) ProxiesStyle proxiesStyle,
|
||||
@Default(defaultWindowProps) WindowProps windowProps,
|
||||
@Default(defaultClashConfig) ClashConfig patchClashConfig,
|
||||
|
||||
@@ -19,7 +19,6 @@ mixin _$AppState {
|
||||
bool get isInit => throw _privateConstructorUsedError;
|
||||
PageLabel get pageLabel => throw _privateConstructorUsedError;
|
||||
List<Package> get packages => throw _privateConstructorUsedError;
|
||||
ColorSchemes get colorSchemes => throw _privateConstructorUsedError;
|
||||
int get sortNum => throw _privateConstructorUsedError;
|
||||
Size get viewSize => throw _privateConstructorUsedError;
|
||||
Map<String, Map<String, int?>> get delayMap =>
|
||||
@@ -53,7 +52,6 @@ abstract class $AppStateCopyWith<$Res> {
|
||||
{bool isInit,
|
||||
PageLabel pageLabel,
|
||||
List<Package> packages,
|
||||
ColorSchemes colorSchemes,
|
||||
int sortNum,
|
||||
Size viewSize,
|
||||
Map<String, Map<String, int?>> delayMap,
|
||||
@@ -69,8 +67,6 @@ abstract class $AppStateCopyWith<$Res> {
|
||||
FixedList<Traffic> traffics,
|
||||
Traffic totalTraffic,
|
||||
bool needApply});
|
||||
|
||||
$ColorSchemesCopyWith<$Res> get colorSchemes;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -91,7 +87,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
|
||||
Object? isInit = null,
|
||||
Object? pageLabel = null,
|
||||
Object? packages = null,
|
||||
Object? colorSchemes = null,
|
||||
Object? sortNum = null,
|
||||
Object? viewSize = null,
|
||||
Object? delayMap = null,
|
||||
@@ -121,10 +116,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
|
||||
? _value.packages
|
||||
: packages // ignore: cast_nullable_to_non_nullable
|
||||
as List<Package>,
|
||||
colorSchemes: null == colorSchemes
|
||||
? _value.colorSchemes
|
||||
: colorSchemes // ignore: cast_nullable_to_non_nullable
|
||||
as ColorSchemes,
|
||||
sortNum: null == sortNum
|
||||
? _value.sortNum
|
||||
: sortNum // ignore: cast_nullable_to_non_nullable
|
||||
@@ -187,16 +178,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ColorSchemesCopyWith<$Res> get colorSchemes {
|
||||
return $ColorSchemesCopyWith<$Res>(_value.colorSchemes, (value) {
|
||||
return _then(_value.copyWith(colorSchemes: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -211,7 +192,6 @@ abstract class _$$AppStateImplCopyWith<$Res>
|
||||
{bool isInit,
|
||||
PageLabel pageLabel,
|
||||
List<Package> packages,
|
||||
ColorSchemes colorSchemes,
|
||||
int sortNum,
|
||||
Size viewSize,
|
||||
Map<String, Map<String, int?>> delayMap,
|
||||
@@ -227,9 +207,6 @@ abstract class _$$AppStateImplCopyWith<$Res>
|
||||
FixedList<Traffic> traffics,
|
||||
Traffic totalTraffic,
|
||||
bool needApply});
|
||||
|
||||
@override
|
||||
$ColorSchemesCopyWith<$Res> get colorSchemes;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -248,7 +225,6 @@ class __$$AppStateImplCopyWithImpl<$Res>
|
||||
Object? isInit = null,
|
||||
Object? pageLabel = null,
|
||||
Object? packages = null,
|
||||
Object? colorSchemes = null,
|
||||
Object? sortNum = null,
|
||||
Object? viewSize = null,
|
||||
Object? delayMap = null,
|
||||
@@ -278,10 +254,6 @@ class __$$AppStateImplCopyWithImpl<$Res>
|
||||
? _value._packages
|
||||
: packages // ignore: cast_nullable_to_non_nullable
|
||||
as List<Package>,
|
||||
colorSchemes: null == colorSchemes
|
||||
? _value.colorSchemes
|
||||
: colorSchemes // ignore: cast_nullable_to_non_nullable
|
||||
as ColorSchemes,
|
||||
sortNum: null == sortNum
|
||||
? _value.sortNum
|
||||
: sortNum // ignore: cast_nullable_to_non_nullable
|
||||
@@ -353,7 +325,6 @@ class _$AppStateImpl implements _AppState {
|
||||
{this.isInit = false,
|
||||
this.pageLabel = PageLabel.dashboard,
|
||||
final List<Package> packages = const [],
|
||||
this.colorSchemes = const ColorSchemes(),
|
||||
this.sortNum = 0,
|
||||
required this.viewSize,
|
||||
final Map<String, Map<String, int?>> delayMap = const {},
|
||||
@@ -389,9 +360,6 @@ class _$AppStateImpl implements _AppState {
|
||||
return EqualUnmodifiableListView(_packages);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final ColorSchemes colorSchemes;
|
||||
@override
|
||||
@JsonKey()
|
||||
final int sortNum;
|
||||
@@ -449,7 +417,7 @@ class _$AppStateImpl implements _AppState {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppState(isInit: $isInit, pageLabel: $pageLabel, packages: $packages, colorSchemes: $colorSchemes, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, needApply: $needApply)';
|
||||
return 'AppState(isInit: $isInit, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, needApply: $needApply)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -461,8 +429,6 @@ class _$AppStateImpl implements _AppState {
|
||||
(identical(other.pageLabel, pageLabel) ||
|
||||
other.pageLabel == pageLabel) &&
|
||||
const DeepCollectionEquality().equals(other._packages, _packages) &&
|
||||
(identical(other.colorSchemes, colorSchemes) ||
|
||||
other.colorSchemes == colorSchemes) &&
|
||||
(identical(other.sortNum, sortNum) || other.sortNum == sortNum) &&
|
||||
(identical(other.viewSize, viewSize) ||
|
||||
other.viewSize == viewSize) &&
|
||||
@@ -489,28 +455,26 @@ class _$AppStateImpl implements _AppState {
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
runtimeType,
|
||||
isInit,
|
||||
pageLabel,
|
||||
const DeepCollectionEquality().hash(_packages),
|
||||
colorSchemes,
|
||||
sortNum,
|
||||
viewSize,
|
||||
const DeepCollectionEquality().hash(_delayMap),
|
||||
const DeepCollectionEquality().hash(_groups),
|
||||
checkIpNum,
|
||||
brightness,
|
||||
runTime,
|
||||
const DeepCollectionEquality().hash(_providers),
|
||||
localIp,
|
||||
requests,
|
||||
version,
|
||||
logs,
|
||||
traffics,
|
||||
totalTraffic,
|
||||
needApply
|
||||
]);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
isInit,
|
||||
pageLabel,
|
||||
const DeepCollectionEquality().hash(_packages),
|
||||
sortNum,
|
||||
viewSize,
|
||||
const DeepCollectionEquality().hash(_delayMap),
|
||||
const DeepCollectionEquality().hash(_groups),
|
||||
checkIpNum,
|
||||
brightness,
|
||||
runTime,
|
||||
const DeepCollectionEquality().hash(_providers),
|
||||
localIp,
|
||||
requests,
|
||||
version,
|
||||
logs,
|
||||
traffics,
|
||||
totalTraffic,
|
||||
needApply);
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -526,7 +490,6 @@ abstract class _AppState implements AppState {
|
||||
{final bool isInit,
|
||||
final PageLabel pageLabel,
|
||||
final List<Package> packages,
|
||||
final ColorSchemes colorSchemes,
|
||||
final int sortNum,
|
||||
required final Size viewSize,
|
||||
final Map<String, Map<String, int?>> delayMap,
|
||||
@@ -550,8 +513,6 @@ abstract class _AppState implements AppState {
|
||||
@override
|
||||
List<Package> get packages;
|
||||
@override
|
||||
ColorSchemes get colorSchemes;
|
||||
@override
|
||||
int get sortNum;
|
||||
@override
|
||||
Size get viewSize;
|
||||
|
||||
@@ -342,6 +342,7 @@ const _$LogLevelEnumMap = {
|
||||
LogLevel.warning: 'warning',
|
||||
LogLevel.error: 'error',
|
||||
LogLevel.silent: 'silent',
|
||||
LogLevel.app: 'app',
|
||||
};
|
||||
|
||||
const _$FindProcessModeEnumMap = {
|
||||
|
||||
@@ -22,6 +22,7 @@ const _$LogLevelEnumMap = {
|
||||
LogLevel.warning: 'warning',
|
||||
LogLevel.error: 'error',
|
||||
LogLevel.silent: 'silent',
|
||||
LogLevel.app: 'app',
|
||||
};
|
||||
|
||||
_$PackageImpl _$$PackageImplFromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@@ -37,6 +37,7 @@ mixin _$AppSettingProps {
|
||||
bool get disclaimerAccepted => throw _privateConstructorUsedError;
|
||||
bool get minimizeOnExit => throw _privateConstructorUsedError;
|
||||
bool get hidden => throw _privateConstructorUsedError;
|
||||
bool get developerMode => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this AppSettingProps to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -70,7 +71,8 @@ abstract class $AppSettingPropsCopyWith<$Res> {
|
||||
bool showLabel,
|
||||
bool disclaimerAccepted,
|
||||
bool minimizeOnExit,
|
||||
bool hidden});
|
||||
bool hidden,
|
||||
bool developerMode});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -103,6 +105,7 @@ class _$AppSettingPropsCopyWithImpl<$Res, $Val extends AppSettingProps>
|
||||
Object? disclaimerAccepted = null,
|
||||
Object? minimizeOnExit = null,
|
||||
Object? hidden = null,
|
||||
Object? developerMode = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
locale: freezed == locale
|
||||
@@ -165,6 +168,10 @@ class _$AppSettingPropsCopyWithImpl<$Res, $Val extends AppSettingProps>
|
||||
? _value.hidden
|
||||
: hidden // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
developerMode: null == developerMode
|
||||
? _value.developerMode
|
||||
: developerMode // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -193,7 +200,8 @@ abstract class _$$AppSettingPropsImplCopyWith<$Res>
|
||||
bool showLabel,
|
||||
bool disclaimerAccepted,
|
||||
bool minimizeOnExit,
|
||||
bool hidden});
|
||||
bool hidden,
|
||||
bool developerMode});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -224,6 +232,7 @@ class __$$AppSettingPropsImplCopyWithImpl<$Res>
|
||||
Object? disclaimerAccepted = null,
|
||||
Object? minimizeOnExit = null,
|
||||
Object? hidden = null,
|
||||
Object? developerMode = null,
|
||||
}) {
|
||||
return _then(_$AppSettingPropsImpl(
|
||||
locale: freezed == locale
|
||||
@@ -286,6 +295,10 @@ class __$$AppSettingPropsImplCopyWithImpl<$Res>
|
||||
? _value.hidden
|
||||
: hidden // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
developerMode: null == developerMode
|
||||
? _value.developerMode
|
||||
: developerMode // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -309,7 +322,8 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
this.showLabel = false,
|
||||
this.disclaimerAccepted = false,
|
||||
this.minimizeOnExit = true,
|
||||
this.hidden = false})
|
||||
this.hidden = false,
|
||||
this.developerMode = false})
|
||||
: _dashboardWidgets = dashboardWidgets;
|
||||
|
||||
factory _$AppSettingPropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -366,10 +380,13 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool hidden;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool developerMode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden)';
|
||||
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -402,7 +419,9 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
other.disclaimerAccepted == disclaimerAccepted) &&
|
||||
(identical(other.minimizeOnExit, minimizeOnExit) ||
|
||||
other.minimizeOnExit == minimizeOnExit) &&
|
||||
(identical(other.hidden, hidden) || other.hidden == hidden));
|
||||
(identical(other.hidden, hidden) || other.hidden == hidden) &&
|
||||
(identical(other.developerMode, developerMode) ||
|
||||
other.developerMode == developerMode));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -423,7 +442,8 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
|
||||
showLabel,
|
||||
disclaimerAccepted,
|
||||
minimizeOnExit,
|
||||
hidden);
|
||||
hidden,
|
||||
developerMode);
|
||||
|
||||
/// Create a copy of AppSettingProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -459,7 +479,8 @@ abstract class _AppSettingProps implements AppSettingProps {
|
||||
final bool showLabel,
|
||||
final bool disclaimerAccepted,
|
||||
final bool minimizeOnExit,
|
||||
final bool hidden}) = _$AppSettingPropsImpl;
|
||||
final bool hidden,
|
||||
final bool developerMode}) = _$AppSettingPropsImpl;
|
||||
|
||||
factory _AppSettingProps.fromJson(Map<String, dynamic> json) =
|
||||
_$AppSettingPropsImpl.fromJson;
|
||||
@@ -495,6 +516,8 @@ abstract class _AppSettingProps implements AppSettingProps {
|
||||
bool get minimizeOnExit;
|
||||
@override
|
||||
bool get hidden;
|
||||
@override
|
||||
bool get developerMode;
|
||||
|
||||
/// Create a copy of AppSettingProps
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1863,7 +1886,7 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
|
||||
@JsonSerializable()
|
||||
class _$ThemePropsImpl implements _ThemeProps {
|
||||
const _$ThemePropsImpl(
|
||||
{this.primaryColor = defaultPrimaryColor,
|
||||
{this.primaryColor,
|
||||
final List<int> primaryColors = defaultPrimaryColors,
|
||||
this.themeMode = ThemeMode.dark,
|
||||
this.schemeVariant = DynamicSchemeVariant.tonalSpot,
|
||||
@@ -1874,7 +1897,6 @@ class _$ThemePropsImpl implements _ThemeProps {
|
||||
_$$ThemePropsImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final int? primaryColor;
|
||||
final List<int> _primaryColors;
|
||||
@override
|
||||
@@ -1988,6 +2010,7 @@ mixin _$Config {
|
||||
DAV? get dav => throw _privateConstructorUsedError;
|
||||
NetworkProps get networkProps => throw _privateConstructorUsedError;
|
||||
VpnProps get vpnProps => throw _privateConstructorUsedError;
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson)
|
||||
ThemeProps get themeProps => throw _privateConstructorUsedError;
|
||||
ProxiesStyle get proxiesStyle => throw _privateConstructorUsedError;
|
||||
WindowProps get windowProps => throw _privateConstructorUsedError;
|
||||
@@ -2017,7 +2040,7 @@ abstract class $ConfigCopyWith<$Res> {
|
||||
DAV? dav,
|
||||
NetworkProps networkProps,
|
||||
VpnProps vpnProps,
|
||||
ThemeProps themeProps,
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps,
|
||||
ProxiesStyle proxiesStyle,
|
||||
WindowProps windowProps,
|
||||
ClashConfig patchClashConfig});
|
||||
@@ -2214,7 +2237,7 @@ abstract class _$$ConfigImplCopyWith<$Res> implements $ConfigCopyWith<$Res> {
|
||||
DAV? dav,
|
||||
NetworkProps networkProps,
|
||||
VpnProps vpnProps,
|
||||
ThemeProps themeProps,
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps,
|
||||
ProxiesStyle proxiesStyle,
|
||||
WindowProps windowProps,
|
||||
ClashConfig patchClashConfig});
|
||||
@@ -2329,7 +2352,7 @@ class _$ConfigImpl implements _Config {
|
||||
this.dav,
|
||||
this.networkProps = defaultNetworkProps,
|
||||
this.vpnProps = defaultVpnProps,
|
||||
this.themeProps = defaultThemeProps,
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson) required this.themeProps,
|
||||
this.proxiesStyle = defaultProxiesStyle,
|
||||
this.windowProps = defaultWindowProps,
|
||||
this.patchClashConfig = defaultClashConfig})
|
||||
@@ -2374,7 +2397,7 @@ class _$ConfigImpl implements _Config {
|
||||
@JsonKey()
|
||||
final VpnProps vpnProps;
|
||||
@override
|
||||
@JsonKey()
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson)
|
||||
final ThemeProps themeProps;
|
||||
@override
|
||||
@JsonKey()
|
||||
@@ -2464,7 +2487,8 @@ abstract class _Config implements Config {
|
||||
final DAV? dav,
|
||||
final NetworkProps networkProps,
|
||||
final VpnProps vpnProps,
|
||||
final ThemeProps themeProps,
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson)
|
||||
required final ThemeProps themeProps,
|
||||
final ProxiesStyle proxiesStyle,
|
||||
final WindowProps windowProps,
|
||||
final ClashConfig patchClashConfig}) = _$ConfigImpl;
|
||||
@@ -2489,6 +2513,7 @@ abstract class _Config implements Config {
|
||||
@override
|
||||
VpnProps get vpnProps;
|
||||
@override
|
||||
@JsonKey(fromJson: ThemeProps.safeFromJson)
|
||||
ThemeProps get themeProps;
|
||||
@override
|
||||
ProxiesStyle get proxiesStyle;
|
||||
|
||||
@@ -26,6 +26,7 @@ _$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
|
||||
disclaimerAccepted: json['disclaimerAccepted'] as bool? ?? false,
|
||||
minimizeOnExit: json['minimizeOnExit'] as bool? ?? true,
|
||||
hidden: json['hidden'] as bool? ?? false,
|
||||
developerMode: json['developerMode'] as bool? ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AppSettingPropsImplToJson(
|
||||
@@ -48,10 +49,12 @@ Map<String, dynamic> _$$AppSettingPropsImplToJson(
|
||||
'disclaimerAccepted': instance.disclaimerAccepted,
|
||||
'minimizeOnExit': instance.minimizeOnExit,
|
||||
'hidden': instance.hidden,
|
||||
'developerMode': instance.developerMode,
|
||||
};
|
||||
|
||||
const _$DashboardWidgetEnumMap = {
|
||||
DashboardWidget.networkSpeed: 'networkSpeed',
|
||||
DashboardWidget.outboundModeV2: 'outboundModeV2',
|
||||
DashboardWidget.outboundMode: 'outboundMode',
|
||||
DashboardWidget.trafficUsage: 'trafficUsage',
|
||||
DashboardWidget.networkDetection: 'networkDetection',
|
||||
@@ -221,8 +224,7 @@ const _$ProxyCardTypeEnumMap = {
|
||||
|
||||
_$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ThemePropsImpl(
|
||||
primaryColor:
|
||||
(json['primaryColor'] as num?)?.toInt() ?? defaultPrimaryColor,
|
||||
primaryColor: (json['primaryColor'] as num?)?.toInt(),
|
||||
primaryColors: (json['primaryColors'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList() ??
|
||||
@@ -287,9 +289,8 @@ _$ConfigImpl _$$ConfigImplFromJson(Map<String, dynamic> json) => _$ConfigImpl(
|
||||
vpnProps: json['vpnProps'] == null
|
||||
? defaultVpnProps
|
||||
: VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?),
|
||||
themeProps: json['themeProps'] == null
|
||||
? defaultThemeProps
|
||||
: ThemeProps.fromJson(json['themeProps'] as Map<String, dynamic>?),
|
||||
themeProps:
|
||||
ThemeProps.safeFromJson(json['themeProps'] as Map<String, Object?>?),
|
||||
proxiesStyle: json['proxiesStyle'] == null
|
||||
? defaultProxiesStyle
|
||||
: ProxiesStyle.fromJson(
|
||||
|
||||
@@ -345,6 +345,7 @@ const _$ActionMethodEnumMap = {
|
||||
ActionMethod.getCountryCode: 'getCountryCode',
|
||||
ActionMethod.getMemory: 'getMemory',
|
||||
ActionMethod.getProfile: 'getProfile',
|
||||
ActionMethod.crash: 'crash',
|
||||
ActionMethod.setFdMap: 'setFdMap',
|
||||
ActionMethod.setProcessMap: 'setProcessMap',
|
||||
ActionMethod.setState: 'setState',
|
||||
|
||||
@@ -129,6 +129,16 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
|
||||
controller: _pageController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: navigationItems.length,
|
||||
// onPageChanged: (index) {
|
||||
// debouncer.call(DebounceTag.pageChange, () {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// if (_pageIndex != index) {
|
||||
// final pageLabel = navigationItems[index].label;
|
||||
// _toPage(pageLabel, true);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
itemBuilder: (_, index) {
|
||||
final navigationItem = navigationItems[index];
|
||||
return KeepScope(
|
||||
|
||||
@@ -252,21 +252,6 @@ class CurrentPageLabel extends _$CurrentPageLabel
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class AppSchemes extends _$AppSchemes with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
ColorSchemes build() {
|
||||
return globalState.appState.colorSchemes;
|
||||
}
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
colorSchemes: value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class SortNum extends _$SortNum with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
|
||||
@@ -247,21 +247,6 @@ final currentPageLabelProvider =
|
||||
);
|
||||
|
||||
typedef _$CurrentPageLabel = AutoDisposeNotifier<PageLabel>;
|
||||
String _$appSchemesHash() => r'748f48f23539a879a92f318a21e1266b1df56aae';
|
||||
|
||||
/// See also [AppSchemes].
|
||||
@ProviderFor(AppSchemes)
|
||||
final appSchemesProvider =
|
||||
AutoDisposeNotifierProvider<AppSchemes, ColorSchemes>.internal(
|
||||
AppSchemes.new,
|
||||
name: r'appSchemesProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$appSchemesHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AppSchemes = AutoDisposeNotifier<ColorSchemes>;
|
||||
String _$sortNumHash() => r'0f85ebbc77124020eaccf988c6ac9d86a7f34d7e';
|
||||
|
||||
/// See also [SortNum].
|
||||
|
||||
@@ -1765,7 +1765,7 @@ class _GetProfileOverrideDataProviderElement
|
||||
String get profileId => (origin as GetProfileOverrideDataProvider).profileId;
|
||||
}
|
||||
|
||||
String _$genColorSchemeHash() => r'a27ccae9b5c11d47cd46804f42f8e9dc7946a6c2';
|
||||
String _$genColorSchemeHash() => r'b18f15c938a8132ee4ed02cdfc02f3b9f01724e2';
|
||||
|
||||
/// See also [genColorScheme].
|
||||
@ProviderFor(genColorScheme)
|
||||
@@ -1780,12 +1780,12 @@ class GenColorSchemeFamily extends Family<ColorScheme> {
|
||||
GenColorSchemeProvider call(
|
||||
Brightness brightness, {
|
||||
Color? color,
|
||||
bool isOverride = false,
|
||||
bool ignoreConfig = false,
|
||||
}) {
|
||||
return GenColorSchemeProvider(
|
||||
brightness,
|
||||
color: color,
|
||||
isOverride: isOverride,
|
||||
ignoreConfig: ignoreConfig,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1796,7 +1796,7 @@ class GenColorSchemeFamily extends Family<ColorScheme> {
|
||||
return call(
|
||||
provider.brightness,
|
||||
color: provider.color,
|
||||
isOverride: provider.isOverride,
|
||||
ignoreConfig: provider.ignoreConfig,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1821,13 +1821,13 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
|
||||
GenColorSchemeProvider(
|
||||
Brightness brightness, {
|
||||
Color? color,
|
||||
bool isOverride = false,
|
||||
bool ignoreConfig = false,
|
||||
}) : this._internal(
|
||||
(ref) => genColorScheme(
|
||||
ref as GenColorSchemeRef,
|
||||
brightness,
|
||||
color: color,
|
||||
isOverride: isOverride,
|
||||
ignoreConfig: ignoreConfig,
|
||||
),
|
||||
from: genColorSchemeProvider,
|
||||
name: r'genColorSchemeProvider',
|
||||
@@ -1840,7 +1840,7 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
|
||||
GenColorSchemeFamily._allTransitiveDependencies,
|
||||
brightness: brightness,
|
||||
color: color,
|
||||
isOverride: isOverride,
|
||||
ignoreConfig: ignoreConfig,
|
||||
);
|
||||
|
||||
GenColorSchemeProvider._internal(
|
||||
@@ -1852,12 +1852,12 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
|
||||
required super.from,
|
||||
required this.brightness,
|
||||
required this.color,
|
||||
required this.isOverride,
|
||||
required this.ignoreConfig,
|
||||
}) : super.internal();
|
||||
|
||||
final Brightness brightness;
|
||||
final Color? color;
|
||||
final bool isOverride;
|
||||
final bool ignoreConfig;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
@@ -1874,7 +1874,7 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
|
||||
debugGetCreateSourceHash: null,
|
||||
brightness: brightness,
|
||||
color: color,
|
||||
isOverride: isOverride,
|
||||
ignoreConfig: ignoreConfig,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1889,7 +1889,7 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
|
||||
return other is GenColorSchemeProvider &&
|
||||
other.brightness == brightness &&
|
||||
other.color == color &&
|
||||
other.isOverride == isOverride;
|
||||
other.ignoreConfig == ignoreConfig;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1897,7 +1897,7 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, brightness.hashCode);
|
||||
hash = _SystemHash.combine(hash, color.hashCode);
|
||||
hash = _SystemHash.combine(hash, isOverride.hashCode);
|
||||
hash = _SystemHash.combine(hash, ignoreConfig.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
@@ -1912,8 +1912,8 @@ mixin GenColorSchemeRef on AutoDisposeProviderRef<ColorScheme> {
|
||||
/// The parameter `color` of this provider.
|
||||
Color? get color;
|
||||
|
||||
/// The parameter `isOverride` of this provider.
|
||||
bool get isOverride;
|
||||
/// The parameter `ignoreConfig` of this provider.
|
||||
bool get ignoreConfig;
|
||||
}
|
||||
|
||||
class _GenColorSchemeProviderElement
|
||||
@@ -1925,7 +1925,7 @@ class _GenColorSchemeProviderElement
|
||||
@override
|
||||
Color? get color => (origin as GenColorSchemeProvider).color;
|
||||
@override
|
||||
bool get isOverride => (origin as GenColorSchemeProvider).isOverride;
|
||||
bool get ignoreConfig => (origin as GenColorSchemeProvider).ignoreConfig;
|
||||
}
|
||||
|
||||
String _$profileOverrideStateHash() =>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
@@ -511,7 +513,7 @@ ColorScheme genColorScheme(
|
||||
Ref ref,
|
||||
Brightness brightness, {
|
||||
Color? color,
|
||||
bool isOverride = false,
|
||||
bool ignoreConfig = false,
|
||||
}) {
|
||||
final vm2 = ref.watch(
|
||||
themeSettingProvider.select(
|
||||
@@ -521,11 +523,17 @@ ColorScheme genColorScheme(
|
||||
),
|
||||
),
|
||||
);
|
||||
if (color == null && (isOverride == true || vm2.a == null)) {
|
||||
final colorSchemes = ref.watch(appSchemesProvider);
|
||||
return colorSchemes.getColorSchemeForBrightness(
|
||||
brightness,
|
||||
vm2.b,
|
||||
if (color == null && (ignoreConfig == true || vm2.a == null)) {
|
||||
// if (globalState.corePalette != null) {
|
||||
// return globalState.corePalette!.toColorScheme(brightness: brightness);
|
||||
// }
|
||||
return ColorScheme.fromSeed(
|
||||
seedColor: globalState.corePalette
|
||||
?.toColorScheme(brightness: brightness)
|
||||
.primary ??
|
||||
globalState.accentColor,
|
||||
brightness: brightness,
|
||||
dynamicSchemeVariant: vm2.b,
|
||||
);
|
||||
}
|
||||
return ColorScheme.fromSeed(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/theme.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
@@ -9,6 +10,7 @@ import 'package:fl_clash/plugins/service.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:material_color_utilities/palettes/core_palette.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
@@ -31,6 +33,8 @@ class GlobalState {
|
||||
Function? updateCurrentDelayDebounce;
|
||||
late Measure measure;
|
||||
late CommonTheme theme;
|
||||
late Color accentColor;
|
||||
CorePalette? corePalette;
|
||||
DateTime? startTime;
|
||||
UpdateTasks tasks = [];
|
||||
final navigatorKey = GlobalKey<NavigatorState>();
|
||||
@@ -55,9 +59,17 @@ class GlobalState {
|
||||
traffics: FixedList(30),
|
||||
totalTraffic: Traffic(),
|
||||
);
|
||||
await _initDynamicColor();
|
||||
await init();
|
||||
}
|
||||
|
||||
_initDynamicColor() async {
|
||||
try {
|
||||
corePalette = await DynamicColorPlugin.getCorePalette();
|
||||
accentColor = await DynamicColorPlugin.getAccentColor() ?? Color(defaultPrimaryColor);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
init() async {
|
||||
packageInfo = await PackageInfo.fromPlatform();
|
||||
config = await preferences.getConfig() ??
|
||||
|
||||
@@ -103,7 +103,7 @@ class PrimaryColorBox extends ConsumerWidget {
|
||||
genColorSchemeProvider(
|
||||
themeData.brightness,
|
||||
color: primaryColor,
|
||||
isOverride: true,
|
||||
ignoreConfig: true,
|
||||
),
|
||||
);
|
||||
return Theme(
|
||||
|
||||
73
lib/widgets/container.dart
Normal file
73
lib/widgets/container.dart
Normal file
@@ -0,0 +1,73 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CommonSafeArea extends StatelessWidget {
|
||||
const CommonSafeArea({
|
||||
super.key,
|
||||
this.left = true,
|
||||
this.top = true,
|
||||
this.right = true,
|
||||
this.bottom = true,
|
||||
this.minimum = EdgeInsets.zero,
|
||||
this.maintainBottomViewPadding = false,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final bool left;
|
||||
|
||||
final bool top;
|
||||
|
||||
final bool right;
|
||||
|
||||
final bool bottom;
|
||||
|
||||
final EdgeInsets minimum;
|
||||
|
||||
final bool maintainBottomViewPadding;
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasMediaQuery(context));
|
||||
EdgeInsets padding = MediaQuery.paddingOf(context);
|
||||
final height = MediaQuery.of(context).size.height;
|
||||
if (maintainBottomViewPadding) {
|
||||
padding = padding.copyWith(
|
||||
bottom: MediaQuery.viewPaddingOf(context).bottom,
|
||||
);
|
||||
}
|
||||
final double realPaddingTop = padding.top > height * 0.5 ? 0 : padding.top;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: math.max(left ? padding.left : 0.0, minimum.left),
|
||||
top: math.max(top ? realPaddingTop : 0.0, minimum.top),
|
||||
right: math.max(right ? padding.right : 0.0, minimum.right),
|
||||
bottom: math.max(bottom ? padding.bottom : 0.0, minimum.bottom),
|
||||
),
|
||||
child: MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeLeft: left,
|
||||
removeTop: top,
|
||||
removeRight: right,
|
||||
removeBottom: bottom,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
.add(FlagProperty('left', value: left, ifTrue: 'avoid left padding'));
|
||||
properties
|
||||
.add(FlagProperty('top', value: top, ifTrue: 'avoid top padding'));
|
||||
properties.add(
|
||||
FlagProperty('right', value: right, ifTrue: 'avoid right padding'));
|
||||
properties.add(
|
||||
FlagProperty('bottom', value: bottom, ifTrue: 'avoid bottom padding'));
|
||||
}
|
||||
}
|
||||
@@ -36,11 +36,13 @@ class FadeBox extends StatelessWidget {
|
||||
class FadeThroughBox extends StatelessWidget {
|
||||
final Widget child;
|
||||
final Alignment? alignment;
|
||||
final EdgeInsets? margin;
|
||||
|
||||
const FadeThroughBox({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.alignment,
|
||||
this.margin
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -52,6 +54,7 @@ class FadeThroughBox extends StatelessWidget {
|
||||
secondaryAnimation,
|
||||
) {
|
||||
return Container(
|
||||
margin: margin,
|
||||
alignment: alignment ?? Alignment.centerLeft,
|
||||
child: FadeThroughTransition(
|
||||
animation: animation,
|
||||
|
||||
@@ -466,6 +466,32 @@ List<Widget> generateSection({
|
||||
];
|
||||
}
|
||||
|
||||
Widget generateSectionV2({
|
||||
String? title,
|
||||
required Iterable<Widget> items,
|
||||
List<Widget>? actions,
|
||||
bool separated = true,
|
||||
}) {
|
||||
return Column(
|
||||
children: [
|
||||
if (items.isNotEmpty && title != null)
|
||||
ListHeader(
|
||||
title: title,
|
||||
actions: actions,
|
||||
),
|
||||
CommonCard(
|
||||
radius: 18,
|
||||
type: CommonCardType.filled,
|
||||
child: Column(
|
||||
children: [
|
||||
...items,
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> generateInfoSection({
|
||||
required Info info,
|
||||
required Iterable<Widget> items,
|
||||
@@ -497,4 +523,4 @@ Widget generateListView(List<Widget> items) {
|
||||
bottom: 16,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,8 +439,12 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
floatingActionButton: ValueListenableBuilder<Widget?>(
|
||||
valueListenable: _floatingActionButton,
|
||||
builder: (_, value, __) {
|
||||
return FadeScaleBox(
|
||||
child: value ?? SizedBox(),
|
||||
return IntrinsicWidth(
|
||||
child: IntrinsicHeight(
|
||||
child: FadeScaleBox(
|
||||
child: value ?? SizedBox(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -369,6 +369,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
}
|
||||
|
||||
_handleDelete(int index) async {
|
||||
await _transformCompleter?.future;
|
||||
_preTransformState();
|
||||
final indexWhere = _tempIndexList.indexWhere((i) => i == index);
|
||||
_tempIndexList.removeAt(indexWhere);
|
||||
@@ -484,9 +485,24 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
Widget _draggableWrap({
|
||||
required Widget childWhenDragging,
|
||||
required Widget feedback,
|
||||
required Widget target,
|
||||
required Widget item,
|
||||
required int index,
|
||||
}) {
|
||||
final target = DragTarget<int>(
|
||||
builder: (_, __, ___) {
|
||||
return AbsorbPointer(
|
||||
child: item,
|
||||
);
|
||||
},
|
||||
onWillAcceptWithDetails: (_) {
|
||||
debouncer.call(
|
||||
DebounceTag.handleWill,
|
||||
_handleWill,
|
||||
args: [index],
|
||||
);
|
||||
return false;
|
||||
},
|
||||
);
|
||||
final shakeTarget = ValueListenableBuilder(
|
||||
valueListenable: _dragIndexNotifier,
|
||||
builder: (_, dragIndex, child) {
|
||||
@@ -539,7 +555,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
valueListenable: isEditNotifier,
|
||||
builder: (_, isEdit, child) {
|
||||
if (!isEdit) {
|
||||
return target;
|
||||
return item;
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
@@ -558,12 +574,10 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
_itemContexts[index] = context;
|
||||
final childWhenDragging = ActivateBox(
|
||||
child: Opacity(
|
||||
opacity: 0.3,
|
||||
opacity: 0.6,
|
||||
child: _sizeBoxWrap(
|
||||
CommonCard(
|
||||
child: Container(
|
||||
color: context.colorScheme.secondaryContainer,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
index,
|
||||
),
|
||||
@@ -580,25 +594,11 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
index,
|
||||
),
|
||||
);
|
||||
final target = DragTarget<int>(
|
||||
builder: (_, __, ___) {
|
||||
return child;
|
||||
},
|
||||
onWillAcceptWithDetails: (_) {
|
||||
debouncer.call(
|
||||
DebounceTag.handleWill,
|
||||
_handleWill,
|
||||
args: [index],
|
||||
);
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
return _wrapTransform(
|
||||
_draggableWrap(
|
||||
childWhenDragging: childWhenDragging,
|
||||
feedback: feedback,
|
||||
target: target,
|
||||
item: child,
|
||||
index: index,
|
||||
),
|
||||
index,
|
||||
@@ -666,8 +666,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
|
||||
crossAxisSpacing: widget.crossAxisSpacing,
|
||||
mainAxisSpacing: widget.mainAxisSpacing,
|
||||
children: [
|
||||
for (int i = 0; i < children.length; i++)
|
||||
_builderItem(i),
|
||||
for (int i = 0; i < children.length; i++) _builderItem(i),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
1100
lib/widgets/tab.dart
Normal file
1100
lib/widgets/tab.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -30,3 +30,5 @@ export 'scroll.dart';
|
||||
export 'dialog.dart';
|
||||
export 'effect.dart';
|
||||
export 'palette.dart';
|
||||
export 'tab.dart';
|
||||
export 'container.dart';
|
||||
|
||||
@@ -843,7 +843,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: fl_clash
|
||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||
publish_to: 'none'
|
||||
version: 0.8.82+202504182
|
||||
version: 0.8.83+202504221
|
||||
environment:
|
||||
sdk: '>=3.1.0 <4.0.0'
|
||||
|
||||
@@ -53,6 +53,7 @@ dependencies:
|
||||
flutter_riverpod: ^2.6.1
|
||||
riverpod_annotation: ^2.6.1
|
||||
riverpod: ^2.6.1
|
||||
material_color_utilities: ^0.11.1
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
Reference in New Issue
Block a user