Compare commits
1 Commits
v0.8.91
...
v0.8.88-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e5379dfc4 |
116
.github/workflows/build.yaml
vendored
@@ -22,14 +22,14 @@ jobs:
|
||||
os: ubuntu-22.04
|
||||
arch: amd64
|
||||
- platform: macos
|
||||
os: macos-15-intel
|
||||
os: macos-13
|
||||
arch: amd64
|
||||
- platform: macos
|
||||
os: macos-latest
|
||||
arch: arm64
|
||||
# - platform: windows
|
||||
# os: windows-11-arm
|
||||
# arch: arm64
|
||||
- platform: windows
|
||||
os: windows-11-arm
|
||||
arch: arm64
|
||||
- platform: linux
|
||||
os: ubuntu-24.04-arm
|
||||
arch: arm64
|
||||
@@ -64,25 +64,22 @@ jobs:
|
||||
cache-dependency-path: |
|
||||
core/go.sum
|
||||
|
||||
- name: Setup Flutter Master
|
||||
if: startsWith(matrix.os, 'windows-11-arm') || startsWith(matrix.os, 'ubuntu-24.04-arm')
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'master'
|
||||
cache: true
|
||||
- name: Setup Flutter
|
||||
if: ${{ !(startsWith(matrix.os, 'windows-11-arm') || startsWith(matrix.os, 'ubuntu-24.04-arm')) }}
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: stable
|
||||
flutter-version: 3.35.7
|
||||
cache: true
|
||||
- name: Setup Flutter With Other
|
||||
if: startsWith(matrix.os, 'windows-11-arm') || startsWith(matrix.os, 'ubuntu-24.04-arm')
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: master
|
||||
flutter-version: 3.35.7
|
||||
channel: 'stable'
|
||||
cache: true
|
||||
# flutter-version: 3.29.3
|
||||
|
||||
- name: Get Flutter Dependency
|
||||
run: |
|
||||
flutter --version
|
||||
flutter pub get
|
||||
run: flutter pub get
|
||||
|
||||
- name: Setup
|
||||
run: dart setup.dart ${{ matrix.platform }} ${{ matrix.arch && format('--arch {0}', matrix.arch) }} ${{ env.IS_STABLE == 'true' && '--env stable' || '' }}
|
||||
@@ -107,26 +104,34 @@ jobs:
|
||||
- name: Generate
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
run: |
|
||||
last_ver=$(grep -m1 '^## ' CHANGELOG.md 2>/dev/null | sed 's/^## //')
|
||||
|
||||
tags=($(git tag --merged HEAD --sort=-creatordate))
|
||||
|
||||
temp="NEW_CHANGELOG.md" > "$temp"
|
||||
|
||||
for i in "${!tags[@]}"; do
|
||||
curr="${tags[i]}"
|
||||
[[ "$curr" == "$last_ver" ]] && break
|
||||
|
||||
prev="${tags[i+1]}"
|
||||
range="${prev:+$prev..}$curr"
|
||||
|
||||
echo -e "## $curr\n" >> "$temp"
|
||||
git log --no-merges --pretty=format:"%B" "$range" | \
|
||||
awk '!/Update changelog/ && NF {print "- " $0 "\n"}' >> "$temp"
|
||||
tags=($(git tag --merged $(git rev-parse HEAD) --sort=-creatordate))
|
||||
preTag=$(grep -oP '^## \K.*' CHANGELOG.md | head -n 1)
|
||||
currentTag=""
|
||||
for ((i = 0; i <= ${#tags[@]}; i++)); do
|
||||
if (( i < ${#tags[@]} )); then
|
||||
tag=${tags[$i]}
|
||||
else
|
||||
tag=""
|
||||
fi
|
||||
if [ -n "$currentTag" ]; then
|
||||
if [ "$(echo -e "$currentTag\n$preTag" | sort -V | head -n 1)" == "$currentTag" ]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
if [ -n "$currentTag" ]; then
|
||||
echo "## $currentTag" >> NEW_CHANGELOG.md
|
||||
echo "" >> NEW_CHANGELOG.md
|
||||
if [ -n "$tag" ]; then
|
||||
git log --pretty=format:"%B" "$tag..$currentTag" | awk 'NF {print "- " $0} !NF {print ""}' >> NEW_CHANGELOG.md
|
||||
else
|
||||
git log --pretty=format:"%B" "$currentTag" | awk 'NF {print "- " $0} !NF {print ""}' >> NEW_CHANGELOG.md
|
||||
fi
|
||||
echo "" >> NEW_CHANGELOG.md
|
||||
fi
|
||||
currentTag=$tag
|
||||
done
|
||||
[ -f CHANGELOG.md ] && cat CHANGELOG.md >> "$temp"
|
||||
|
||||
mv "$temp" CHANGELOG.md
|
||||
cat CHANGELOG.md >> NEW_CHANGELOG.md
|
||||
cat NEW_CHANGELOG.md > CHANGELOG.md
|
||||
|
||||
- name: Commit
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
@@ -176,24 +181,31 @@ jobs:
|
||||
|
||||
- name: Generate release.md
|
||||
run: |
|
||||
tags=($(git tag --merged HEAD --sort=-creatordate))
|
||||
preTag=$(curl -s "https://api.github.com/repos/chen08209/FlClash/releases/latest" | \
|
||||
sed -nE 's/.*"tag_name": "([^"]+)".*/\1/p')
|
||||
|
||||
[ -z "$preTag" ] && preTag=""
|
||||
|
||||
out="release.md" > "$out"
|
||||
|
||||
for i in "${!tags[@]}"; do
|
||||
curr="${tags[i]}"
|
||||
[[ "$curr" == "$preTag" ]] && break
|
||||
|
||||
prev="${tags[i+1]}"
|
||||
range="${prev:+$prev..}$curr"
|
||||
|
||||
git log --no-merges --pretty=format:"%B" "$range" | \
|
||||
awk '!/Update changelog/ && NF {print "- " $0 "\n"}' >> "$out"
|
||||
tags=($(git tag --merged $(git rev-parse HEAD) --sort=-creatordate))
|
||||
preTag=$(curl --silent "https://api.github.com/repos/chen08209/FlClash/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")' || echo "")
|
||||
currentTag=""
|
||||
for ((i = 0; i <= ${#tags[@]}; i++)); do
|
||||
if (( i < ${#tags[@]} )); then
|
||||
tag=${tags[$i]}
|
||||
else
|
||||
tag=""
|
||||
fi
|
||||
if [ -n "$currentTag" ]; then
|
||||
if [ "$(echo -e "$currentTag\n$preTag" | sort -V | head -n 1)" == "$currentTag" ]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
if [ -n "$currentTag" ]; then
|
||||
if [ -n "$tag" ]; then
|
||||
git log --pretty=format:"%B" "$tag..$currentTag" | awk 'NF {print "- " $0} !NF {print ""}' >> release.md
|
||||
else
|
||||
git log --pretty=format:"%B" "$currentTag" | awk 'NF {print "- " $0} !NF {print ""}' >> release.md
|
||||
fi
|
||||
echo "" >> release.md
|
||||
fi
|
||||
currentTag=$tag
|
||||
done
|
||||
|
||||
- name: Push to telegram
|
||||
env:
|
||||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
|
||||
16
.gitignore
vendored
@@ -45,19 +45,11 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
/android/**/.cxx
|
||||
/android/**/build
|
||||
/android/common/**/.**/
|
||||
/android/common/local.*
|
||||
/android/core/**/includes/
|
||||
/android/core/**/cmake-build-*/
|
||||
/android/core/**/jniLibs/
|
||||
|
||||
|
||||
#FlClash
|
||||
|
||||
#libclash
|
||||
/libclash/
|
||||
/android/app/src/main/jniLibs/
|
||||
/services/helper/target
|
||||
/macos/**/Package.resolved
|
||||
devtools_options.yaml
|
||||
|
||||
#jniLibs
|
||||
/android/app/src/main/jniLibs/
|
||||
|
||||
4
.gitmodules
vendored
@@ -6,9 +6,5 @@
|
||||
path = plugins/flutter_distributor
|
||||
url = git@github.com:chen08209/flutter_distributor.git
|
||||
branch = FlClash
|
||||
[submodule "plugins/tray_manager"]
|
||||
path = plugins/tray_manager
|
||||
url = git@github.com:chen08209/tray_manager.git
|
||||
branch = main
|
||||
|
||||
|
||||
|
||||
36
CHANGELOG.md
@@ -1,39 +1,3 @@
|
||||
## v0.8.90
|
||||
|
||||
- Fix android tile service
|
||||
|
||||
- Support append system DNS
|
||||
|
||||
- Fix some issues
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.89
|
||||
|
||||
- Fix some issues
|
||||
|
||||
- Optimize Windows service mode
|
||||
|
||||
- Update core
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.88
|
||||
|
||||
- Add android separates the core process
|
||||
|
||||
- Support core status check and force restart
|
||||
|
||||
- Optimize proxies page and access page
|
||||
|
||||
- Update flutter and pub dependencies
|
||||
|
||||
- Update go version
|
||||
|
||||
- Optimize more details
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.87
|
||||
|
||||
- Optimize desktop view
|
||||
|
||||
@@ -20,9 +20,7 @@ class BroadcastReceiver : BroadcastReceiver() {
|
||||
|
||||
BroadcastAction.SERVICE_DESTROYED.action -> {
|
||||
GlobalState.log("Receiver service destroyed")
|
||||
GlobalState.launch {
|
||||
State.handleStopServiceAction()
|
||||
}
|
||||
State.handleStopServiceAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.follow.clash
|
||||
import com.follow.clash.common.ServiceDelegate
|
||||
import com.follow.clash.common.formatString
|
||||
import com.follow.clash.common.intent
|
||||
import com.follow.clash.service.IAckInterface
|
||||
import com.follow.clash.service.ICallbackInterface
|
||||
import com.follow.clash.service.IEventInterface
|
||||
import com.follow.clash.service.IRemoteInterface
|
||||
@@ -45,11 +44,8 @@ object Service {
|
||||
return delegate.useService {
|
||||
it.invokeAction(
|
||||
data, object : ICallbackInterface.Stub() {
|
||||
override fun onResult(
|
||||
result: ByteArray?, isSuccess: Boolean, ack: IAckInterface?
|
||||
) {
|
||||
override fun onResult(result: ByteArray?, isSuccess: Boolean) {
|
||||
res.add(result ?: byteArrayOf())
|
||||
ack?.onAck()
|
||||
if (isSuccess) {
|
||||
cb(res.formatString())
|
||||
}
|
||||
@@ -65,24 +61,24 @@ object Service {
|
||||
return delegate.useService {
|
||||
it.setEventListener(
|
||||
when (cb != null) {
|
||||
true -> object : IEventInterface.Stub() {
|
||||
override fun onEvent(
|
||||
id: String, data: ByteArray?, isSuccess: Boolean, ack: IAckInterface?
|
||||
) {
|
||||
if (results[id] == null) {
|
||||
results[id] = mutableListOf()
|
||||
}
|
||||
results[id]?.add(data ?: byteArrayOf())
|
||||
ack?.onAck()
|
||||
if (isSuccess) {
|
||||
cb(results[id]?.formatString())
|
||||
results.remove(id)
|
||||
true -> object : IEventInterface.Stub() {
|
||||
override fun onEvent(
|
||||
id: String, data: ByteArray?, isSuccess: Boolean
|
||||
) {
|
||||
if (results[id] == null) {
|
||||
results[id] = mutableListOf()
|
||||
}
|
||||
results[id]?.add(data ?: byteArrayOf())
|
||||
if (isSuccess) {
|
||||
cb(results[id]?.formatString())
|
||||
results.remove(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false -> null
|
||||
})
|
||||
false -> null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,11 +100,11 @@ object Service {
|
||||
|
||||
private suspend fun awaitIResultInterface(
|
||||
block: (IResultInterface) -> Unit
|
||||
): Long = suspendCancellableCoroutine { continuation ->
|
||||
): Unit = suspendCancellableCoroutine { continuation ->
|
||||
val callback = object : IResultInterface.Stub() {
|
||||
override fun onResult(time: Long) {
|
||||
override fun onResult() {
|
||||
if (continuation.isActive) {
|
||||
continuation.resume(time)
|
||||
continuation.resume(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,25 +119,19 @@ object Service {
|
||||
}
|
||||
|
||||
|
||||
suspend fun startService(options: VpnOptions, runTime: Long): Long {
|
||||
return delegate.useService {
|
||||
suspend fun startService(options: VpnOptions) {
|
||||
delegate.useService {
|
||||
awaitIResultInterface { callback ->
|
||||
it.startService(options, runTime, callback)
|
||||
it.startService(options, callback)
|
||||
}
|
||||
}.getOrNull() ?: 0L
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun stopService(): Long {
|
||||
return delegate.useService {
|
||||
suspend fun stopService() {
|
||||
delegate.useService {
|
||||
awaitIResultInterface { callback ->
|
||||
it.stopService(callback)
|
||||
}
|
||||
}.getOrNull() ?: 0L
|
||||
}
|
||||
|
||||
suspend fun getRunTime(): Long {
|
||||
return delegate.useService {
|
||||
it.runTime
|
||||
}.getOrNull() ?: 0L
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,53 +52,25 @@ object State {
|
||||
action?.invoke()
|
||||
}
|
||||
|
||||
suspend fun handleSyncState() {
|
||||
runLock.withLock {
|
||||
try {
|
||||
Service.bind()
|
||||
runTime = Service.getRunTime()
|
||||
val runState = when (runTime == 0L) {
|
||||
true -> RunState.STOP
|
||||
false -> RunState.START
|
||||
}
|
||||
runStateFlow.tryEmit(runState)
|
||||
} catch (_: Exception) {
|
||||
runStateFlow.tryEmit(RunState.STOP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun handleStartServiceAction() {
|
||||
runLock.withLock {
|
||||
if (runStateFlow.value != RunState.STOP) {
|
||||
return
|
||||
}
|
||||
tilePlugin?.handleStart()
|
||||
if (flutterEngine != null) {
|
||||
return
|
||||
}
|
||||
startServiceWithEngine()
|
||||
tilePlugin?.handleStart()
|
||||
if (flutterEngine != null) {
|
||||
return
|
||||
}
|
||||
|
||||
startServiceWithEngine()
|
||||
}
|
||||
|
||||
suspend fun handleStopServiceAction() {
|
||||
runLock.withLock {
|
||||
if (runStateFlow.value != RunState.START) {
|
||||
return
|
||||
}
|
||||
tilePlugin?.handleStop()
|
||||
if (flutterEngine != null || serviceFlutterEngine != null) {
|
||||
return
|
||||
}
|
||||
handleStopService()
|
||||
fun handleStopServiceAction() {
|
||||
tilePlugin?.handleStop()
|
||||
if (flutterEngine != null || serviceFlutterEngine != null) {
|
||||
return
|
||||
}
|
||||
handleStopService()
|
||||
}
|
||||
|
||||
fun handleStartService() {
|
||||
val appPlugin = flutterEngine?.plugin<AppPlugin>()
|
||||
if (appPlugin != null) {
|
||||
appPlugin.requestNotificationsPermission {
|
||||
appPlugin?.requestNotificationsPermission {
|
||||
startService()
|
||||
}
|
||||
return
|
||||
@@ -106,23 +78,8 @@ object State {
|
||||
startService()
|
||||
}
|
||||
|
||||
fun handleStopService() {
|
||||
GlobalState.launch {
|
||||
runLock.withLock {
|
||||
if (runStateFlow.value != RunState.START) {
|
||||
return@launch
|
||||
}
|
||||
runStateFlow.tryEmit(RunState.PENDING)
|
||||
runTime = Service.stopService()
|
||||
runStateFlow.tryEmit(RunState.STOP)
|
||||
}
|
||||
destroyServiceEngine()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun destroyServiceEngine() {
|
||||
runLock.withLock {
|
||||
GlobalState.log("Destroy service engine")
|
||||
withContext(Dispatchers.Main) {
|
||||
runCatching {
|
||||
serviceFlutterEngine?.destroy()
|
||||
@@ -132,24 +89,17 @@ object State {
|
||||
}
|
||||
}
|
||||
|
||||
private fun startServiceWithEngine() {
|
||||
GlobalState.launch {
|
||||
runLock.withLock {
|
||||
if (runStateFlow.value != RunState.STOP) {
|
||||
return@launch
|
||||
}
|
||||
GlobalState.log("Create service engine")
|
||||
withContext(Dispatchers.Main) {
|
||||
serviceFlutterEngine?.destroy()
|
||||
serviceFlutterEngine = FlutterEngine(GlobalState.application)
|
||||
serviceFlutterEngine?.plugins?.add(ServicePlugin())
|
||||
serviceFlutterEngine?.plugins?.add(AppPlugin())
|
||||
serviceFlutterEngine?.plugins?.add(TilePlugin())
|
||||
val dartEntrypoint = DartExecutor.DartEntrypoint(
|
||||
FlutterInjector.instance().flutterLoader().findAppBundlePath(), "_service"
|
||||
)
|
||||
serviceFlutterEngine?.dartExecutor?.executeDartEntrypoint(dartEntrypoint)
|
||||
}
|
||||
suspend fun startServiceWithEngine() {
|
||||
runLock.withLock {
|
||||
withContext(Dispatchers.Main) {
|
||||
serviceFlutterEngine = FlutterEngine(GlobalState.application)
|
||||
serviceFlutterEngine?.plugins?.add(ServicePlugin())
|
||||
serviceFlutterEngine?.plugins?.add(AppPlugin())
|
||||
serviceFlutterEngine?.plugins?.add(TilePlugin())
|
||||
val dartEntrypoint = DartExecutor.DartEntrypoint(
|
||||
FlutterInjector.instance().flutterLoader().findAppBundlePath(), "_service"
|
||||
)
|
||||
serviceFlutterEngine?.dartExecutor?.executeDartEntrypoint(dartEntrypoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,22 +107,41 @@ object State {
|
||||
private fun startService() {
|
||||
GlobalState.launch {
|
||||
runLock.withLock {
|
||||
if (runStateFlow.value != RunState.STOP) {
|
||||
if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.START) {
|
||||
return@launch
|
||||
}
|
||||
runStateFlow.tryEmit(RunState.PENDING)
|
||||
if (servicePlugin == null) {
|
||||
return@launch
|
||||
}
|
||||
val options = servicePlugin?.handleGetVpnOptions() ?: return@launch
|
||||
val options = servicePlugin?.handleGetVpnOptions()
|
||||
if (options == null) {
|
||||
return@launch
|
||||
}
|
||||
appPlugin?.prepare(options.enable) {
|
||||
runTime = Service.startService(options, runTime)
|
||||
Service.startService(options)
|
||||
runTime = System.currentTimeMillis()
|
||||
runStateFlow.tryEmit(RunState.START)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun handleStopService() {
|
||||
GlobalState.launch {
|
||||
runLock.withLock {
|
||||
if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.STOP) {
|
||||
return@launch
|
||||
}
|
||||
runStateFlow.tryEmit(RunState.PENDING)
|
||||
Service.stopService()
|
||||
runStateFlow.tryEmit(RunState.STOP)
|
||||
runTime = 0
|
||||
}
|
||||
destroyServiceEngine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@ class TempActivity : Activity(),
|
||||
}
|
||||
|
||||
QuickAction.STOP.action -> {
|
||||
launch {
|
||||
State.handleStopServiceAction()
|
||||
}
|
||||
State.handleStopServiceAction()
|
||||
}
|
||||
|
||||
QuickAction.TOGGLE.action -> {
|
||||
|
||||
@@ -31,7 +31,6 @@ class TileService : TileService() {
|
||||
scope?.cancel()
|
||||
scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||
scope?.launch {
|
||||
State.handleSyncState()
|
||||
State.runStateFlow.collect {
|
||||
updateTile(it)
|
||||
}
|
||||
@@ -45,7 +44,8 @@ class TileService : TileService() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startActivityAndCollapse(pendingIntent)
|
||||
} else {
|
||||
@Suppress("DEPRECATION") startActivityAndCollapse(intent)
|
||||
@Suppress("DEPRECATION")
|
||||
startActivityAndCollapse(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) = when (call.method) {
|
||||
"init" -> {
|
||||
handleInit(call, result)
|
||||
handleInit(result)
|
||||
}
|
||||
|
||||
"shutdown" -> {
|
||||
@@ -131,16 +131,11 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
|
||||
}
|
||||
}
|
||||
|
||||
fun handleInit(call: MethodCall, result: MethodChannel.Result) {
|
||||
fun handleInit(result: MethodChannel.Result) {
|
||||
Service.bind()
|
||||
launch {
|
||||
val needSetEventListener = call.arguments<Boolean>() ?: false
|
||||
when (needSetEventListener) {
|
||||
true -> Service.setEventListener {
|
||||
handleSendEvent(it)
|
||||
}
|
||||
|
||||
false -> Service.setEventListener(null)
|
||||
Service.setEventListener {
|
||||
handleSendEvent(it)
|
||||
}.onSuccess {
|
||||
result.success("")
|
||||
}.onFailure {
|
||||
@@ -152,9 +147,6 @@ class ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler,
|
||||
}
|
||||
|
||||
private fun handleGetRunTime(result: MethodChannel.Result) {
|
||||
launch {
|
||||
State.handleSyncState()
|
||||
result.success(State.runTime)
|
||||
}
|
||||
return result.success(State.runTime)
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="240"
|
||||
android:viewportHeight="240">
|
||||
<group android:scaleX="0.924"
|
||||
android:scaleY="0.924"
|
||||
android:translateX="9.12"
|
||||
android:translateY="9.12">
|
||||
<group android:scaleX="0.63461536"
|
||||
android:scaleY="0.63461536"
|
||||
android:translateX="45.96154"
|
||||
android:translateY="43.846153">
|
||||
<path
|
||||
android:pathData="M60.65,89.6L154.18,35.6A18,18 107.59,0 1,178.77 42.19L178.77,42.19A18,18 107.59,0 1,172.18 66.78L78.65,120.78A18,18 106.67,0 1,54.06 114.19L54.06,114.19A18,18 106.67,0 1,60.65 89.6z"
|
||||
android:fillColor="#6666FB"/>
|
||||
<path
|
||||
android:pathData="M84.65,131.17L131.42,104.17A18,18 107.83,0 1,156 110.76L156,110.76A18,18 107.83,0 1,149.42 135.35L102.65,162.35A18,18 106.67,0 1,78.06 155.76L78.06,155.76A18,18 106.67,0 1,84.65 131.17z"
|
||||
android:fillColor="#336AB6"/>
|
||||
<path
|
||||
android:pathData="M108.65,172.74L108.65,172.74A18,18 116.03,0 1,133.24 179.33L133.24,179.33A18,18 116.03,0 1,126.65 203.92L126.65,203.92A18,18 116.03,0 1,102.06 197.33L102.06,197.33A18,18 116.03,0 1,108.65 172.74z"
|
||||
android:fillColor="#5CA8E9"/>
|
||||
</group>
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="240"
|
||||
android:viewportHeight="240">
|
||||
<group android:scaleX="0.924"
|
||||
android:scaleY="0.924"
|
||||
android:translateX="9.12"
|
||||
android:translateY="9.12">
|
||||
<group android:scaleX="0.63461536"
|
||||
android:scaleY="0.63461536"
|
||||
android:translateX="45.96154"
|
||||
android:translateY="43.846153">
|
||||
<path
|
||||
android:pathData="M60.65,89.6L154.18,35.6A18,18 107.59,0 1,178.77 42.19L178.77,42.19A18,18 107.59,0 1,172.18 66.78L78.65,120.78A18,18 106.67,0 1,54.06 114.19L54.06,114.19A18,18 106.67,0 1,60.65 89.6z"
|
||||
android:fillColor="#6666FB"/>
|
||||
<path
|
||||
android:pathData="M84.65,131.17L131.42,104.17A18,18 107.83,0 1,156 110.76L156,110.76A18,18 107.83,0 1,149.42 135.35L102.65,162.35A18,18 106.67,0 1,78.06 155.76L78.06,155.76A18,18 106.67,0 1,84.65 131.17z"
|
||||
android:fillColor="#336AB6"/>
|
||||
<path
|
||||
android:pathData="M108.65,172.74L108.65,172.74A18,18 116.03,0 1,133.24 179.33L133.24,179.33A18,18 116.03,0 1,126.65 203.92L126.65,203.92A18,18 116.03,0 1,102.06 197.33L102.06,197.33A18,18 116.03,0 1,108.65 172.74z"
|
||||
android:fillColor="#5CA8E9"/>
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
@@ -220,6 +220,7 @@ val Long.formatBytes: String
|
||||
fun String.chunkedForAidl(charset: Charset = Charsets.UTF_8): List<ByteArray> {
|
||||
val allBytes = toByteArray(charset)
|
||||
val total = allBytes.size
|
||||
|
||||
val maxBytes = when {
|
||||
total <= 100 * 1024 -> total
|
||||
total <= 1024 * 1024 -> 64 * 1024
|
||||
|
||||
@@ -11,7 +11,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@@ -60,9 +59,7 @@ class ServiceDelegate<T>(
|
||||
withTimeout(timeoutMillis) {
|
||||
val state = serviceState.filterNotNull().first()
|
||||
state.first?.let {
|
||||
withContext(Dispatchers.Default) {
|
||||
block(it)
|
||||
}
|
||||
block(it)
|
||||
} ?: throw Exception(state.second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,25 @@ message("CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
|
||||
if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||
add_compile_options(-O3 -flto -g0 -fno-exceptions -fno-rtti)
|
||||
add_link_options(-flto -Wl,--gc-sections,--strip-all)
|
||||
# set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
||||
add_compile_options(-O3)
|
||||
|
||||
add_compile_options(-flto)
|
||||
|
||||
add_compile_options(-g0)
|
||||
|
||||
add_compile_options(-ffunction-sections -fdata-sections)
|
||||
|
||||
add_compile_options(-fno-exceptions -fno-rtti)
|
||||
|
||||
add_link_options(
|
||||
-flto
|
||||
-Wl,--gc-sections
|
||||
-Wl,--strip-all
|
||||
-Wl,--exclude-libs=ALL
|
||||
)
|
||||
|
||||
add_compile_options(-fvisibility=hidden -fvisibility-inlines-hidden)
|
||||
endif ()
|
||||
|
||||
set(LIB_CLASH_PATH "${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libclash.so")
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
android:process=":remote">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="proxy" />
|
||||
android:value="service" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// IAckInterface.aidl
|
||||
package com.follow.clash.service;
|
||||
|
||||
import com.follow.clash.service.IAckInterface;
|
||||
|
||||
interface IAckInterface {
|
||||
oneway void onAck();
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
// ICallbackInterface.aidl
|
||||
package com.follow.clash.service;
|
||||
|
||||
import com.follow.clash.service.IAckInterface;
|
||||
|
||||
interface ICallbackInterface {
|
||||
oneway void onResult(in byte[] data,in boolean isSuccess, in IAckInterface ack);
|
||||
oneway void onResult(in byte[] data,in boolean isSuccess);
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
// IEventInterface.aidl
|
||||
package com.follow.clash.service;
|
||||
|
||||
import com.follow.clash.service.IAckInterface;
|
||||
|
||||
interface IEventInterface {
|
||||
oneway void onEvent(in String id, in byte[] data,in boolean isSuccess, in IAckInterface ack);
|
||||
oneway void onEvent(in String id, in byte[] data,in boolean isSuccess);
|
||||
}
|
||||
@@ -10,9 +10,8 @@ import com.follow.clash.service.models.NotificationParams;
|
||||
interface IRemoteInterface {
|
||||
void invokeAction(in String data, in ICallbackInterface callback);
|
||||
void updateNotificationParams(in NotificationParams params);
|
||||
void startService(in VpnOptions options, in long runTime, in IResultInterface result);
|
||||
void startService(in VpnOptions options, in IResultInterface result);
|
||||
void stopService(in IResultInterface result);
|
||||
void setEventListener(in IEventInterface event);
|
||||
void setCrashlytics(in boolean enable);
|
||||
long getRunTime();
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
package com.follow.clash.service;
|
||||
|
||||
interface IResultInterface {
|
||||
oneway void onResult(in long runTime);
|
||||
oneway void onResult();
|
||||
}
|
||||
@@ -50,11 +50,7 @@ class CommonService : Service(), IBaseService,
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
try {
|
||||
loader.load()
|
||||
} catch (_: Exception) {
|
||||
stop()
|
||||
}
|
||||
loader.load()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
|
||||
@@ -8,22 +8,23 @@ import com.follow.clash.common.ServiceDelegate
|
||||
import com.follow.clash.common.chunkedForAidl
|
||||
import com.follow.clash.common.intent
|
||||
import com.follow.clash.core.Core
|
||||
import com.follow.clash.service.State.delegate
|
||||
import com.follow.clash.service.State.intent
|
||||
import com.follow.clash.service.State.runLock
|
||||
import com.follow.clash.service.models.NotificationParams
|
||||
import com.follow.clash.service.models.VpnOptions
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.util.UUID
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class RemoteService : Service(),
|
||||
CoroutineScope by CoroutineScope(SupervisorJob() + Dispatchers.Default) {
|
||||
private var delegate: ServiceDelegate<IBaseService>? = null
|
||||
private var intent: Intent? = null
|
||||
|
||||
val runLock = Mutex()
|
||||
|
||||
private fun handleStopService(result: IResultInterface) {
|
||||
launch {
|
||||
runLock.withLock {
|
||||
@@ -31,8 +32,7 @@ class RemoteService : Service(),
|
||||
service.stop()
|
||||
delegate?.unbind()
|
||||
}
|
||||
State.runTime = 0
|
||||
result.onResult(0)
|
||||
result.onResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class RemoteService : Service(),
|
||||
delegate = null
|
||||
}
|
||||
|
||||
private fun handleStartService(runTime: Long, result: IResultInterface) {
|
||||
private fun handleStartService(result: IResultInterface) {
|
||||
launch {
|
||||
runLock.withLock {
|
||||
val nextIntent = when (State.options?.enable == true) {
|
||||
@@ -65,11 +65,7 @@ class RemoteService : Service(),
|
||||
delegate?.useService { service ->
|
||||
service.start()
|
||||
}
|
||||
State.runTime = when (runTime != 0L) {
|
||||
true -> runTime
|
||||
false -> System.currentTimeMillis()
|
||||
}
|
||||
result.onResult(State.runTime)
|
||||
result.onResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,22 +73,11 @@ class RemoteService : Service(),
|
||||
private val binder = object : IRemoteInterface.Stub() {
|
||||
override fun invokeAction(data: String, callback: ICallbackInterface) {
|
||||
Core.invokeAction(data) {
|
||||
launch {
|
||||
runCatching {
|
||||
val chunks = it?.chunkedForAidl() ?: listOf()
|
||||
for ((index, chunk) in chunks.withIndex()) {
|
||||
suspendCancellableCoroutine { cont ->
|
||||
callback.onResult(
|
||||
chunk,
|
||||
index == chunks.lastIndex,
|
||||
object : IAckInterface.Stub() {
|
||||
override fun onAck() {
|
||||
cont.resume(Unit)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
val chunks = it?.chunkedForAidl() ?: listOf()
|
||||
val totalSize = chunks.size
|
||||
chunks.forEachIndexed { index, chunk ->
|
||||
callback.onResult(chunk, totalSize - 1 == index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,14 +87,12 @@ class RemoteService : Service(),
|
||||
State.notificationParamsFlow.tryEmit(params)
|
||||
}
|
||||
|
||||
|
||||
override fun startService(
|
||||
options: VpnOptions,
|
||||
runtime: Long,
|
||||
result: IResultInterface,
|
||||
) {
|
||||
State.options = options
|
||||
handleStartService(runtime, result)
|
||||
handleStartService(result)
|
||||
}
|
||||
|
||||
override fun stopService(result: IResultInterface) {
|
||||
@@ -117,27 +100,15 @@ class RemoteService : Service(),
|
||||
}
|
||||
|
||||
override fun setEventListener(eventListener: IEventInterface?) {
|
||||
GlobalState.log("RemoveEventListener ${eventListener == null}")
|
||||
GlobalState.log("isRemoveEventListener is ${eventListener == null}")
|
||||
when (eventListener != null) {
|
||||
true -> Core.callSetEventListener {
|
||||
launch {
|
||||
runCatching {
|
||||
val id = UUID.randomUUID().toString()
|
||||
val chunks = it?.chunkedForAidl() ?: listOf()
|
||||
for ((index, chunk) in chunks.withIndex()) {
|
||||
suspendCancellableCoroutine { cont ->
|
||||
eventListener.onEvent(
|
||||
id,
|
||||
chunk,
|
||||
index == chunks.lastIndex,
|
||||
object : IAckInterface.Stub() {
|
||||
override fun onAck() {
|
||||
cont.resume(Unit)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
val id = UUID.randomUUID().toString()
|
||||
val chunks = it?.chunkedForAidl() ?: listOf()
|
||||
val totalSize = chunks.size
|
||||
chunks.forEachIndexed { index, chunk ->
|
||||
eventListener.onEvent(id, chunk, totalSize - 1 == index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,18 +120,9 @@ class RemoteService : Service(),
|
||||
override fun setCrashlytics(enable: Boolean) {
|
||||
GlobalState.setCrashlytics(enable)
|
||||
}
|
||||
|
||||
override fun getRunTime(): Long {
|
||||
return State.runTime
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder {
|
||||
return binder
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
GlobalState.log("Remote service destroy")
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,12 @@
|
||||
package com.follow.clash.service
|
||||
|
||||
import android.content.Intent
|
||||
import com.follow.clash.common.ServiceDelegate
|
||||
import com.follow.clash.service.models.NotificationParams
|
||||
import com.follow.clash.service.models.VpnOptions
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
||||
object State {
|
||||
var options: VpnOptions? = null
|
||||
var notificationParamsFlow: MutableStateFlow<NotificationParams?> = MutableStateFlow(
|
||||
NotificationParams()
|
||||
)
|
||||
|
||||
val runLock = Mutex()
|
||||
var runTime: Long = 0L
|
||||
|
||||
var delegate: ServiceDelegate<IBaseService>? = null
|
||||
|
||||
var intent: Intent? = null
|
||||
}
|
||||
@@ -213,7 +213,6 @@ class VpnService : SystemVpnService(), IBaseService,
|
||||
allowBypass()
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.systemProxy) {
|
||||
GlobalState.log("Open http proxy")
|
||||
setHttpProxy(
|
||||
ProxyInfo.buildDirectProxy(
|
||||
"127.0.0.1", options.port, options.bypassDomain
|
||||
@@ -234,13 +233,9 @@ class VpnService : SystemVpnService(), IBaseService,
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
try {
|
||||
loader.load()
|
||||
State.options?.let {
|
||||
handleStart(it)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
stop()
|
||||
loader.load()
|
||||
State.options?.let {
|
||||
handleStart(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.follow.clash.service.models
|
||||
|
||||
import com.follow.clash.common.GlobalState
|
||||
import com.follow.clash.common.formatBytes
|
||||
import com.follow.clash.core.Core
|
||||
import com.google.gson.Gson
|
||||
@@ -18,8 +17,7 @@ fun Core.getSpeedTrafficText(onlyStatisticsProxy: Boolean): String {
|
||||
val res = getTraffic(onlyStatisticsProxy)
|
||||
val traffic = Gson().fromJson(res, Traffic::class.java)
|
||||
return traffic.speedText
|
||||
} catch (e: Exception) {
|
||||
GlobalState.log(e.message + "")
|
||||
} catch (_: Exception) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,9 @@ class NotificationModule(private val service: Service) : Module() {
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
override fun onInstall() {
|
||||
State.notificationParamsFlow.value?.let {
|
||||
update(it.extended)
|
||||
}
|
||||
scope.launch {
|
||||
val screenFlow = service.receiveBroadcastFlow {
|
||||
addAction(Intent.ACTION_SCREEN_ON)
|
||||
@@ -66,12 +69,6 @@ class NotificationModule(private val service: Service) : Module() {
|
||||
.collect { (params, _) ->
|
||||
update(params!!)
|
||||
}
|
||||
|
||||
State.notificationParamsFlow.value?.let {
|
||||
update(it.extended)
|
||||
} ?: run {
|
||||
update(NotificationParams().extended)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -340,8 +340,6 @@
|
||||
"none": "none",
|
||||
"basicConfig": "Basic configuration",
|
||||
"basicConfigDesc": "Modify the basic configuration globally",
|
||||
"advancedConfig": "Advanced configuration",
|
||||
"advancedConfigDesc": "Provide diverse configuration options",
|
||||
"selectedCountTitle": "{count} items have been selected",
|
||||
"addRule": "Add rule",
|
||||
"ruleName": "Rule name",
|
||||
@@ -392,7 +390,7 @@
|
||||
"existsTip": "Current {label} already exists",
|
||||
"deleteTip": "Are you sure you want to delete the current {label}?",
|
||||
"deleteMultipTip": "Are you sure you want to delete the selected {label}?",
|
||||
"nullTip": "No {label} yet",
|
||||
"nullTip": "No {label} at the moment",
|
||||
"script": "Script",
|
||||
"color": "Color",
|
||||
"rename": "Rename",
|
||||
@@ -434,37 +432,5 @@
|
||||
"dataCollectionTip": "Data Collection Notice",
|
||||
"dataCollectionContent": "This app uses Firebase Crashlytics to collect crash information to improve app stability.\nThe collected data includes device information and crash details, but does not contain personal sensitive data.\nYou can disable this feature in settings.",
|
||||
"crashlytics": "Crash Analysis",
|
||||
"crashlyticsTip": "When enabled, automatically uploads crash logs without sensitive information when the app crashes",
|
||||
"appendSystemDns": "Append System DNS",
|
||||
"appendSystemDnsTip": "Forcefully append system DNS to the configuration",
|
||||
"editRule": "Edit rule",
|
||||
"overrideMode": "Override mode",
|
||||
"standardModeDesc": "Standard mode, override basic configuration, provide simple rule addition capability",
|
||||
"scriptModeDesc": "Script mode, use external extension scripts, provide one-click override configuration capability",
|
||||
"addedRules": "Added rules",
|
||||
"controlGlobalAddedRules": "Control global added rules",
|
||||
"overrideScript": "Override script",
|
||||
"goToConfigureScript": "Go to configure script",
|
||||
"editGlobalRules": "Edit global rules",
|
||||
"externalFetch": "External fetch",
|
||||
"confirmForceCrashCore": "Are you sure you want to force crash the core?",
|
||||
"confirmClearAllData": "Are you sure you want to clear all data?",
|
||||
"loading": "Loading...",
|
||||
"loadTest": "Load test",
|
||||
"yearsAgo": "{count, plural, =1{1 year ago} other{{count} years ago}}",
|
||||
"monthsAgo": "{count, plural, =1{1 month ago} other{{count} months ago}}",
|
||||
"daysAgo": "{count, plural, =1{1 day ago} other{{count} days ago}}",
|
||||
"hoursAgo": "{count, plural, =1{1 hour ago} other{{count} hours ago}}",
|
||||
"minutesAgo": "{count, plural, =1{1 minute ago} other{{count} minutes ago}}",
|
||||
"justNow": "Just now",
|
||||
"noLongerRemind": "Don't remind again",
|
||||
"accessControlSettings": "Access Control Settings",
|
||||
"turnOn": "Turn On",
|
||||
"turnOff": "Turn Off",
|
||||
"coreConfigChangeDetected": "Core configuration change detected",
|
||||
"reload": "Reload",
|
||||
"vpnConfigChangeDetected": "VPN configuration change detected",
|
||||
"restart": "Restart",
|
||||
"speedStatistics": "Speed statistics",
|
||||
"resetPageChangesTip": "The current page has changes. Are you sure you want to reset?"
|
||||
"crashlyticsTip": "When enabled, automatically uploads crash logs without sensitive information when the app crashes"
|
||||
}
|
||||
@@ -340,8 +340,6 @@
|
||||
"none": "なし",
|
||||
"basicConfig": "基本設定",
|
||||
"basicConfigDesc": "基本設定をグローバルに変更",
|
||||
"advancedConfig": "高度な設定",
|
||||
"advancedConfigDesc": "多様な設定を提供",
|
||||
"selectedCountTitle": "{count} 項目が選択されています",
|
||||
"addRule": "ルールを追加",
|
||||
"ruleName": "ルール名",
|
||||
@@ -393,7 +391,7 @@
|
||||
"existsTip": "現在の{label}は既に存在しています",
|
||||
"deleteTip": "現在の{label}を削除してもよろしいですか?",
|
||||
"deleteMultipTip": "選択された{label}を削除してもよろしいですか?",
|
||||
"nullTip": "まだ{label}はありません",
|
||||
"nullTip": "現在{label}はありません",
|
||||
"script": "スクリプト",
|
||||
"color": "カラー",
|
||||
"rename": "リネーム",
|
||||
@@ -435,37 +433,5 @@
|
||||
"dataCollectionTip": "データ収集説明",
|
||||
"dataCollectionContent": "本アプリはFirebase Crashlyticsを使用してクラッシュ情報を収集し、アプリの安定性を向上させます。\n収集されるデータにはデバイス情報とクラッシュ詳細が含まれますが、個人の機密データは含まれません。\n設定でこの機能を無効にすることができます。",
|
||||
"crashlytics": "クラッシュ分析",
|
||||
"crashlyticsTip": "有効にすると、アプリがクラッシュした際に機密情報を含まないクラッシュログを自動的にアップロードします",
|
||||
"appendSystemDns": "システムDNSを追加",
|
||||
"appendSystemDnsTip": "設定にシステムDNSを強制的に追加します",
|
||||
"editRule": "ルールを編集",
|
||||
"overrideMode": "上書きモード",
|
||||
"standardModeDesc": "標準モード、基本設定を上書きし、シンプルなルール追加機能を提供",
|
||||
"scriptModeDesc": "スクリプトモード、外部拡張スクリプトを使用し、ワンクリックで設定を上書きする機能を提供",
|
||||
"addedRules": "追加ルール",
|
||||
"controlGlobalAddedRules": "グローバル追加ルールを制御",
|
||||
"overrideScript": "上書きスクリプト",
|
||||
"goToConfigureScript": "スクリプト設定に移動",
|
||||
"editGlobalRules": "グローバルルールを編集",
|
||||
"externalFetch": "外部取得",
|
||||
"confirmForceCrashCore": "コアを強制的にクラッシュさせてもよろしいですか?",
|
||||
"confirmClearAllData": "すべてのデータをクリアしてもよろしいですか?",
|
||||
"loading": "読み込み中...",
|
||||
"loadTest": "読み込みテスト",
|
||||
"yearsAgo": "{count}年前",
|
||||
"monthsAgo": "{count}ヶ月前",
|
||||
"daysAgo": "{count}日前",
|
||||
"hoursAgo": "{count}時間前",
|
||||
"minutesAgo": "{count}分前",
|
||||
"justNow": "たった今",
|
||||
"noLongerRemind": "今後表示しない",
|
||||
"accessControlSettings": "アクセス制御設定",
|
||||
"turnOn": "オン",
|
||||
"turnOff": "オフ",
|
||||
"coreConfigChangeDetected": "コア設定の変更が検出されました",
|
||||
"reload": "リロード",
|
||||
"vpnConfigChangeDetected": "VPN設定の変更が検出されました",
|
||||
"restart": "再起動",
|
||||
"speedStatistics": "速度統計",
|
||||
"resetPageChangesTip": "現在のページに変更があります。リセットしてもよろしいですか?"
|
||||
"crashlyticsTip": "有効にすると、アプリがクラッシュした際に機密情報を含まないクラッシュログを自動的にアップロードします"
|
||||
}
|
||||
@@ -340,8 +340,6 @@
|
||||
"none": "Нет",
|
||||
"basicConfig": "Базовая конфигурация",
|
||||
"basicConfigDesc": "Глобальное изменение базовых настроек",
|
||||
"advancedConfig": "Расширенная конфигурация",
|
||||
"advancedConfigDesc": "Предоставляет разнообразные варианты конфигурации",
|
||||
"selectedCountTitle": "Выбрано {count} элементов",
|
||||
"addRule": "Добавить правило",
|
||||
"ruleName": "Название правила",
|
||||
@@ -393,7 +391,7 @@
|
||||
"existsTip": "Текущий {label} уже существует",
|
||||
"deleteTip": "Вы уверены, что хотите удалить текущий {label}?",
|
||||
"deleteMultipTip": "Вы уверены, что хотите удалить выбранные {label}?",
|
||||
"nullTip": "{label} пока отсутствуют",
|
||||
"nullTip": "Сейчас {label} нет",
|
||||
"script": "Скрипт",
|
||||
"color": "Цвет",
|
||||
"rename": "Переименовать",
|
||||
@@ -435,37 +433,5 @@
|
||||
"dataCollectionTip": "Уведомление о сборе данных",
|
||||
"dataCollectionContent": "Это приложение использует Firebase Crashlytics для сбора информации о сбоях nhằm улучшения стабильности приложения.\nСобираемые данные включают информацию об устройстве и подробности о сбоях, но не содержат персональных конфиденциальных данных.\nВы можете отключить эту функцию в настройках.",
|
||||
"crashlytics": "Анализ сбоев",
|
||||
"crashlyticsTip": "При включении автоматически загружает журналы сбоев без конфиденциальной информации, когда приложение выходит из строя",
|
||||
"appendSystemDns": "Добавить системный DNS",
|
||||
"appendSystemDnsTip": "Принудительно добавить системный DNS к конфигурации",
|
||||
"editRule": "Редактировать правило",
|
||||
"overrideMode": "Режим переопределения",
|
||||
"standardModeDesc": "Стандартный режим, переопределение базовой конфигурации, предоставление возможности простого добавления правил",
|
||||
"scriptModeDesc": "Режим скрипта, использование внешних расширяющих скриптов, предоставление возможности переопределения конфигурации одним кликом",
|
||||
"addedRules": "Добавленные правила",
|
||||
"controlGlobalAddedRules": "Управление глобальными добавленными правилами",
|
||||
"overrideScript": "Скрипт переопределения",
|
||||
"goToConfigureScript": "Перейти к настройке скрипта",
|
||||
"editGlobalRules": "Редактировать глобальные правила",
|
||||
"externalFetch": "Внешнее получение",
|
||||
"confirmForceCrashCore": "Вы уверены, что хотите принудительно аварийно завершить работу ядра?",
|
||||
"confirmClearAllData": "Вы уверены, что хотите очистить все данные?",
|
||||
"loading": "Загрузка...",
|
||||
"loadTest": "Тест загрузки",
|
||||
"yearsAgo": "{count, plural, one{{count} год назад} few{{count} года назад} many{{count} лет назад} other{{count} года назад}}",
|
||||
"monthsAgo": "{count, plural, one{{count} месяц назад} few{{count} месяца назад} many{{count} месяцев назад} other{{count} месяца назад}}",
|
||||
"daysAgo": "{count, plural, one{{count} день назад} few{{count} дня назад} many{{count} дней назад} other{{count} дня назад}}",
|
||||
"hoursAgo": "{count, plural, one{{count} час назад} few{{count} часа назад} many{{count} часов назад} other{{count} часа назад}}",
|
||||
"minutesAgo": "{count, plural, one{{count} минута назад} few{{count} минуты назад} many{{count} минут назад} other{{count} минуты назад}}",
|
||||
"justNow": "Только что",
|
||||
"noLongerRemind": "Больше не напоминать",
|
||||
"accessControlSettings": "Настройки контроля доступа",
|
||||
"turnOn": "Включить",
|
||||
"turnOff": "Выключить",
|
||||
"coreConfigChangeDetected": "Обнаружено изменение конфигурации ядра",
|
||||
"reload": "Перезагрузить",
|
||||
"vpnConfigChangeDetected": "Обнаружено изменение конфигурации VPN",
|
||||
"restart": "Перезапустить",
|
||||
"speedStatistics": "Статистика скорости",
|
||||
"resetPageChangesTip": "На текущей странице есть изменения. Вы уверены, что хотите сбросить?"
|
||||
"crashlyticsTip": "При включении автоматически загружает журналы сбоев без конфиденциальной информации, когда приложение выходит из строя"
|
||||
}
|
||||
@@ -340,8 +340,6 @@
|
||||
"none": "无",
|
||||
"basicConfig": "基本配置",
|
||||
"basicConfigDesc": "全局修改基本配置",
|
||||
"advancedConfig": "进阶配置",
|
||||
"advancedConfigDesc": "提供多样化配置",
|
||||
"selectedCountTitle": "已选择 {count} 项",
|
||||
"addRule": "添加规则",
|
||||
"ruleName": "规则名称",
|
||||
@@ -435,37 +433,5 @@
|
||||
"dataCollectionTip": "数据收集说明",
|
||||
"dataCollectionContent": "本应用使用 Firebase Crashlytics 收集崩溃信息以改进应用稳定性。\n收集的数据包括设备信息和崩溃详情,不包含个人敏感数据。\n您可以在设置中关闭此功能。",
|
||||
"crashlytics": "崩溃分析",
|
||||
"crashlyticsTip": "开启后,应用崩溃时自动上传不包含敏感信息的崩溃日志",
|
||||
"appendSystemDns": "追加系统DNS",
|
||||
"appendSystemDnsTip": "强制为配置附加系统DNS",
|
||||
"editRule": "编辑规则",
|
||||
"overrideMode": "覆写模式",
|
||||
"standardModeDesc": "标准模式,覆写基本配置,提供简单追加规则能力",
|
||||
"scriptModeDesc": "脚本模式,使用外部扩展脚本,提供一键覆写配置的能力",
|
||||
"addedRules": "附加规则",
|
||||
"controlGlobalAddedRules": "控制全局附加规则",
|
||||
"overrideScript": "覆写脚本",
|
||||
"goToConfigureScript": "前往配置脚本",
|
||||
"editGlobalRules": "编辑全局规则",
|
||||
"externalFetch": "外部获取",
|
||||
"confirmForceCrashCore": "确定要强制崩溃核心?",
|
||||
"confirmClearAllData": "确定要清除所有数据?",
|
||||
"loading": "加载中...",
|
||||
"loadTest": "加载测试",
|
||||
"yearsAgo": "{count} 年前",
|
||||
"monthsAgo": "{count} 个月前",
|
||||
"daysAgo": "{count} 天前",
|
||||
"hoursAgo": "{count} 小时前",
|
||||
"minutesAgo": "{count} 分钟前",
|
||||
"justNow": "刚刚",
|
||||
"noLongerRemind": "不再提示",
|
||||
"accessControlSettings": "访问控制设置",
|
||||
"turnOn": "开启",
|
||||
"turnOff": "关闭",
|
||||
"coreConfigChangeDetected": "检测到核心配置更改",
|
||||
"reload": "重载",
|
||||
"vpnConfigChangeDetected": "检测到VPN相关配置改动",
|
||||
"restart": "重启",
|
||||
"speedStatistics": "网速统计",
|
||||
"resetPageChangesTip": "当前页面存在更改,确定重置吗?"
|
||||
"crashlyticsTip": "开启后,应用崩溃时自动上传不包含敏感信息的崩溃日志"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -1,23 +0,0 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="75" y="50" width="150" height="180" rx="24" fill="#FDF7FF" stroke="#E8DEF8" stroke-width="2"/>
|
||||
|
||||
<rect x="95" y="90" width="36" height="12" rx="4" fill="#E8DEF8"/>
|
||||
<rect x="140" y="90" width="65" height="12" rx="6" fill="#E8DEF8"/>
|
||||
|
||||
<path d="M95 118H205" stroke="#E8DEF8" stroke-width="2" stroke-dasharray="4 4"/>
|
||||
|
||||
<rect x="95" y="138" width="40" height="12" rx="6" fill="#E8DEF8" opacity="0.7"/>
|
||||
<rect x="145" y="138" width="50" height="12" rx="6" fill="#E8DEF8" opacity="0.5"/>
|
||||
|
||||
<rect x="95" y="162" width="55" height="12" rx="6" fill="#E8DEF8" opacity="0.7"/>
|
||||
<rect x="160" y="162" width="30" height="12" rx="6" fill="#E8DEF8" opacity="0.5"/>
|
||||
|
||||
<g transform="translate(210, 210)">
|
||||
<circle cx="0" cy="0" r="38" fill="#6750A4" stroke="#FDF7FF" stroke-width="6"/>
|
||||
|
||||
<path d="M-10 16V-16M-10 -16L-18 -8M-10 -16L-2 -8" stroke="#FDF7FF" stroke-width="5" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
<path d="M10 -16V16M10 16L2 8M10 16L18 8" stroke="#FDF7FF" stroke-width="5" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,9 +0,0 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="75" y="50" width="150" height="180" rx="24" fill="#FDF7FF" stroke="#E8DEF8" stroke-width="2"/>
|
||||
<rect x="100" y="90" width="100" height="12" rx="6" fill="#E8DEF8"/>
|
||||
<rect x="100" y="115" width="70" height="12" rx="6" fill="#E8DEF8"/>
|
||||
<rect x="100" y="140" width="80" height="12" rx="6" fill="#E8DEF8"/>
|
||||
<rect x="155" y="170" width="80" height="60" rx="12" fill="#6750A4"/>
|
||||
<rect x="150" y="165" width="90" height="18" rx="6" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"/>
|
||||
<rect x="185" y="200" width="20" height="6" rx="3" fill="#FDF7FF" opacity="0.8"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 700 B |
@@ -1,25 +0,0 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M90 200C90 180 100 165 150 165C200 165 210 180 210 200V220C210 231.046 201.046 240 190 240H110C98.9543 240 90 231.046 90 220V200Z"
|
||||
fill="#E8DEF8"/>
|
||||
|
||||
<rect x="75" y="85" width="150" height="100" rx="30" fill="#6750A4"/>
|
||||
|
||||
<rect x="85" y="95" width="130" height="80" rx="22" fill="#6750A4" stroke="#7D66B5" stroke-width="2"/>
|
||||
|
||||
<path d="M110 135 C110 142 118 148 128 148 C138 148 146 142 146 135" stroke="#FDF7FF" stroke-width="4"
|
||||
stroke-linecap="round"/>
|
||||
<path d="M154 135 C154 142 162 148 172 148 C182 148 190 142 190 135" stroke="#FDF7FF" stroke-width="4"
|
||||
stroke-linecap="round"/>
|
||||
<circle cx="150" cy="160" r="4" fill="#E8DEF8" opacity="0.5"/>
|
||||
|
||||
<path d="M150 85 V 65" stroke="#6750A4" stroke-width="4" stroke-linecap="round"/>
|
||||
<circle cx="150" cy="60" r="8" fill="#6750A4"/>
|
||||
<circle cx="150" cy="60" r="3" fill="#FDF7FF"/>
|
||||
|
||||
<path d="M220 70 L235 70 L220 85 H235" stroke="#6750A4" stroke-width="3" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
<path d="M245 40 L255 40 L245 50 H255" stroke="#E8DEF8" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
|
||||
<path d="M90 185 H210" stroke="#000" stroke-width="4" stroke-opacity="0.1" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,37 +0,0 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<path d="M60 94V84C60 72.9543 68.9543 64 80 64H130C141.046 64 150 72.9543 150 84V94H220C231.046 94 240 102.954 240 114V210C240 221.046 231.046 230 220 230H80C68.9543 230 60 221.046 60 210V94Z"
|
||||
fill="#FDF7FF" stroke="#E8DEF8" stroke-width="2"/>
|
||||
|
||||
|
||||
<rect x="90" y="124" width="60" height="12" rx="6" fill="#E8DEF8"/>
|
||||
|
||||
|
||||
<rect x="90" y="154" width="50" height="12" rx="6" fill="#E8DEF8"/>
|
||||
|
||||
<rect x="90" y="184" width="40" height="12" rx="6" fill="#E8DEF8"/>
|
||||
<rect x="180" y="184" width="30" height="12" rx="6" fill="#E8DEF8" opacity="0.6"/>
|
||||
<circle cx="186" cy="190" r="6" fill="#E8DEF8"/>
|
||||
|
||||
<g transform="translate(210, 210)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M0 -32 C-17.67 -32 -32 -17.67 -32 0 C-32 17.67 -17.67 32 0 32 C17.67 32 32 17.67 32 0 C32 -17.67 17.67 -32 0 -32ZM0 -8 C-4.42 -8 -8 -4.42 -8 0 C-8 4.42 -4.42 8 0 8 C4.42 8 8 4.42 8 0 C8 -4.42 4.42 -8 0 -8Z"
|
||||
fill="#6750A4" stroke="#FDF7FF" stroke-width="6"/>
|
||||
|
||||
<rect x="-5" y="-38" width="10" height="12" rx="3" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"/>
|
||||
<rect x="-5" y="26" width="10" height="12" rx="3" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"/>
|
||||
<rect x="-38" y="-5" width="12" height="10" rx="3" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"/>
|
||||
<rect x="26" y="-5" width="12" height="10" rx="3" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"/>
|
||||
<rect x="-5" y="-38" width="10" height="12" rx="3" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"
|
||||
transform="rotate(45)"/>
|
||||
<rect x="-5" y="26" width="10" height="12" rx="3" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"
|
||||
transform="rotate(45)"/>
|
||||
<rect x="-38" y="-5" width="12" height="10" rx="3" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"
|
||||
transform="rotate(45)"/>
|
||||
<rect x="26" y="-5" width="12" height="10" rx="3" fill="#6750A4" stroke="#FDF7FF" stroke-width="2"
|
||||
transform="rotate(45)"/>
|
||||
|
||||
<circle cx="0" cy="0" r="22" fill="#6750A4"/>
|
||||
<circle cx="0" cy="0" r="8" fill="#FDF7FF" opacity="0.8"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,18 +0,0 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="75" y="50" width="150" height="180" rx="24" fill="#FDF7FF" stroke="#E8DEF8" stroke-width="2"/>
|
||||
<rect x="95" y="90" width="110" height="16" rx="4" fill="#E8DEF8"/>
|
||||
<circle cx="105" cy="98" r="3" fill="#FDF7FF"/>
|
||||
<circle cx="115" cy="98" r="3" fill="#FDF7FF"/>
|
||||
<rect x="95" y="120" width="110" height="16" rx="4" fill="#E8DEF8"/>
|
||||
<circle cx="105" cy="128" r="3" fill="#FDF7FF"/>
|
||||
<circle cx="115" cy="128" r="3" fill="#FDF7FF"/>
|
||||
<rect x="95" y="150" width="80" height="16" rx="4" fill="#E8DEF8"/>
|
||||
<circle cx="105" cy="158" r="3" fill="#FDF7FF"/>
|
||||
<circle cx="115" cy="158" r="3" fill="#FDF7FF"/>
|
||||
<circle cx="195" cy="195" r="30" fill="#6750A4"/>
|
||||
<rect x="180" y="193" width="30" height="4" rx="2" fill="#FDF7FF" transform="rotate(45 195 195)"/>
|
||||
<circle cx="183" cy="183" r="4" fill="#FDF7FF"/>
|
||||
<circle cx="207" cy="207" r="4" fill="#FDF7FF"/>
|
||||
<path d="M175 158 H190 V165" stroke="#E8DEF8" stroke-width="2" stroke-linecap="round"/>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,31 +0,0 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<rect x="75" y="50" width="150" height="180" rx="24" fill="#FDF7FF" stroke="#E8DEF8" stroke-width="2"/>
|
||||
|
||||
|
||||
<rect x="95" y="90" width="80" height="12" rx="6" fill="#E8DEF8"/>
|
||||
|
||||
<rect x="185" y="90" width="25" height="12" rx="6" fill="#E8DEF8" opacity="0.7"/>
|
||||
|
||||
|
||||
<rect x="95" y="125" width="60" height="12" rx="6" fill="#E8DEF8"/>
|
||||
<rect x="165" y="125" width="45" height="12" rx="6" fill="#E8DEF8" opacity="0.7"/>
|
||||
|
||||
|
||||
<rect x="95" y="160" width="70" height="12" rx="6" fill="#E8DEF8"/>
|
||||
<rect x="175" y="160" width="35" height="12" rx="6" fill="#E8DEF8" opacity="0.7"/>
|
||||
|
||||
|
||||
<g transform="translate(210, 210)">
|
||||
<circle cx="0" cy="0" r="36" fill="#6750A4" stroke="#FDF7FF" stroke-width="6"/>
|
||||
|
||||
<circle cx="0" cy="0" r="24" stroke="#FDF7FF" stroke-width="3"/>
|
||||
|
||||
<path d="M-24 0C-24 0 -12 8 0 8C12 8 24 0 24 0" stroke="#FDF7FF" stroke-width="3" stroke-linecap="round"
|
||||
stroke-linejoin="round"/>
|
||||
|
||||
<ellipse cx="0" cy="0" rx="10" ry="24" stroke="#FDF7FF" stroke-width="3"/>
|
||||
|
||||
<circle cx="14" cy="-12" r="3" fill="#FDF7FF"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,19 +0,0 @@
|
||||
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="75" y="50" width="150" height="180" rx="24" fill="#FDF7FF" stroke="#E8DEF8" stroke-width="2"/>
|
||||
|
||||
<rect x="100" y="90" width="30" height="12" rx="6" fill="#E8DEF8"/>
|
||||
<rect x="136" y="90" width="50" height="12" rx="6" fill="#E8DEF8"/>
|
||||
|
||||
<rect x="120" y="120" width="80" height="12" rx="6" fill="#E8DEF8"/>
|
||||
|
||||
<rect x="120" y="150" width="50" height="12" rx="6" fill="#E8DEF8"/>
|
||||
|
||||
<rect x="100" y="180" width="20" height="12" rx="6" fill="#E8DEF8"/>
|
||||
|
||||
<g transform="translate(165, 160)">
|
||||
<rect x="0" y="0" width="80" height="80" rx="20" fill="#6750A4" stroke="#FDF7FF" stroke-width="4"/>
|
||||
<path d="M28 30L18 40L28 50" stroke="#FDF7FF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M52 30L62 40L52 50" stroke="#FDF7FF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M46 26L34 54" stroke="#FDF7FF" stroke-width="4" stroke-linecap="round" opacity="0.8"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
BIN
assets/images/icon_black.ico
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
assets/images/icon_black.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/images/icon_white.ico
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/images/icon_white.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
@@ -53,8 +53,8 @@ func handleAction(action *Action, result ActionResult) {
|
||||
result.success(handleShutdown())
|
||||
return
|
||||
case validateConfigMethod:
|
||||
path := action.Data.(string)
|
||||
result.success(handleValidateConfig(path))
|
||||
data := []byte(action.Data.(string))
|
||||
result.success(handleValidateConfig(data))
|
||||
return
|
||||
case updateConfigMethod:
|
||||
data := []byte(action.Data.(string))
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
cp "github.com/metacubex/mihomo/constant/provider"
|
||||
"github.com/metacubex/mihomo/hub"
|
||||
"github.com/metacubex/mihomo/hub/executor"
|
||||
"github.com/metacubex/mihomo/hub/route"
|
||||
"github.com/metacubex/mihomo/listener"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
@@ -25,7 +24,6 @@ import (
|
||||
"github.com/metacubex/mihomo/tunnel"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -264,14 +262,13 @@ func setupConfig(params *SetupParams) error {
|
||||
defer runLock.Unlock()
|
||||
var err error
|
||||
constant.DefaultTestURL = params.TestURL
|
||||
currentConfig, err = executor.ParseWithPath(filepath.Join(constant.Path.HomeDir(), "config.yaml"))
|
||||
currentConfig, err = parseWithPath(filepath.Join(constant.Path.HomeDir(), "config.json"))
|
||||
if err != nil {
|
||||
currentConfig, _ = config.ParseRawConfig(config.DefaultRawConfig())
|
||||
}
|
||||
hub.ApplyConfig(currentConfig)
|
||||
patchSelectGroup(params.SelectedMap)
|
||||
updateListeners()
|
||||
runtime.GC()
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
47
core/go.mod
@@ -18,8 +18,8 @@ require (
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/coreos/go-iptables v0.8.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/enfein/mieru/v3 v3.22.1 // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/enfein/mieru/v3 v3.19.1 // indirect
|
||||
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
|
||||
@@ -33,47 +33,44 @@ require (
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.4.0 // indirect
|
||||
github.com/gofrs/uuid/v5 v5.4.0 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/gofrs/uuid/v5 v5.3.2 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/klauspost/reedsolomon v1.12.3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d // indirect
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a // indirect
|
||||
github.com/metacubex/ascon v0.1.0 // indirect
|
||||
github.com/metacubex/bart v0.26.0 // indirect
|
||||
github.com/metacubex/bart v0.20.5 // indirect
|
||||
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b // indirect
|
||||
github.com/metacubex/blake3 v0.1.0 // indirect
|
||||
github.com/metacubex/chacha v0.1.5 // indirect
|
||||
github.com/metacubex/fswatch v0.1.1 // indirect
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20250919004547-6122b699a301 // indirect
|
||||
github.com/metacubex/kcp-go v0.0.0-20251105084629-8c93f4bf37be // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
|
||||
github.com/metacubex/quic-go v0.55.1-0.20251024060151-bd465f127128 // indirect
|
||||
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 // indirect
|
||||
github.com/metacubex/randv2 v0.2.0 // indirect
|
||||
github.com/metacubex/restls-client-go v0.1.7 // indirect
|
||||
github.com/metacubex/sing v0.5.6 // indirect
|
||||
github.com/metacubex/sing-mux v0.3.4 // indirect
|
||||
github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb // indirect
|
||||
github.com/metacubex/sing v0.5.5 // indirect
|
||||
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac // indirect
|
||||
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb // indirect
|
||||
github.com/metacubex/sing-shadowsocks v0.2.12 // indirect
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.7 // indirect
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.6 // indirect
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.9 // indirect
|
||||
github.com/metacubex/sing-vmess v0.2.4 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.7 // indirect
|
||||
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0 // indirect
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f // indirect
|
||||
github.com/metacubex/smux v0.0.0-20250922175018-15c9a6a78719 // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20251024101424-368b42b59148 // indirect
|
||||
github.com/metacubex/utls v1.8.3 // indirect
|
||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 // indirect
|
||||
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 // indirect
|
||||
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f // indirect
|
||||
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
|
||||
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
|
||||
@@ -81,21 +78,27 @@ require (
|
||||
github.com/openacid/low v0.1.21 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/sagernet/cors v1.2.1 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/samber/lo v1.51.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
|
||||
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
|
||||
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 // indirect
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
|
||||
|
||||
104
core/go.sum
@@ -22,10 +22,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/enfein/mieru/v3 v3.22.1 h1:/XGYYXpEhEJlxosmtbpEJkhtRLHB8IToG7LB8kU2ZDY=
|
||||
github.com/enfein/mieru/v3 v3.22.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/enfein/mieru/v3 v3.19.1 h1:19b9kgFC7oJXX9RLEO5Pi1gO6yek5cWlpK7IJVUoE8I=
|
||||
github.com/enfein/mieru/v3 v3.19.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
|
||||
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=
|
||||
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
|
||||
@@ -44,6 +44,7 @@ github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hH
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
@@ -54,19 +55,20 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
|
||||
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
|
||||
github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=
|
||||
github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/gofrs/uuid/v5 v5.3.2 h1:2jfO8j3XgSwlz/wHqemAEugfnTlikAYHhnqQ8Xh4fE0=
|
||||
github.com/gofrs/uuid/v5 v5.3.2/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
|
||||
@@ -76,22 +78,20 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/reedsolomon v1.12.3 h1:tzUznbfc3OFwJaTebv/QdhnFf2Xvb7gZ24XaHLBPmdc=
|
||||
github.com/klauspost/reedsolomon v1.12.3/go.mod h1:3K5rXwABAvzGeR01r6pWZieUALXO/Tq7bFKGIb4m4WI=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d h1:vAJ0ZT4aO803F1uw2roIA9yH7Sxzox34tVVyye1bz6c=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a h1:c1QSGpacSeQdBdWcEKZKGuWLcqIG2wxHEygAcXuDwS4=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20250820070344-732c0c9d418a/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=
|
||||
github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM=
|
||||
github.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc=
|
||||
github.com/metacubex/bart v0.26.0 h1:d/bBTvVatfVWGfQbiDpYKI1bXUJgjaabB2KpK1Tnk6w=
|
||||
github.com/metacubex/bart v0.26.0/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
||||
github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM=
|
||||
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
||||
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY=
|
||||
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw=
|
||||
github.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY=
|
||||
@@ -102,47 +102,43 @@ github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQux
|
||||
github.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI=
|
||||
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-20250919004547-6122b699a301 h1:N5GExQJqYAH3gOCshpp2u/J3CtNYzMctmlb0xK9wtbQ=
|
||||
github.com/metacubex/gvisor v0.0.0-20250919004547-6122b699a301/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
|
||||
github.com/metacubex/kcp-go v0.0.0-20251105084629-8c93f4bf37be h1:Y7SigZIqfv/+RIA/D7R6EbB9p+brPRoGOM6zobSmRIM=
|
||||
github.com/metacubex/kcp-go v0.0.0-20251105084629-8c93f4bf37be/go.mod h1:HIJZW4QMhbBqXuqC1ly6Hn0TEYT2SzRw58ns1yGhXTs=
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b h1:RUh4OdVPz/jDrM9MQ2ySuqu2aeBqcA8rtfWUYLZ8RtI=
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
|
||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA=
|
||||
github.com/metacubex/quic-go v0.55.1-0.20251024060151-bd465f127128 h1:I1uvJl206/HbkzEAZpLgGkZgUveOZb+P+6oTUj7dN+o=
|
||||
github.com/metacubex/quic-go v0.55.1-0.20251024060151-bd465f127128/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c=
|
||||
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295 h1:8JVlYuE8uSJAvmyCd4TjvDxs57xjb0WxEoaWafK5+qs=
|
||||
github.com/metacubex/quic-go v0.54.1-0.20250730114134-a1ae705fe295/go.mod h1:1lktQFtCD17FZliVypbrDHwbsFSsmz2xz2TRXydvB5c=
|
||||
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
|
||||
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
||||
github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=
|
||||
github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g=
|
||||
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing v0.5.6 h1:mEPDCadsCj3DB8gn+t/EtposlYuALEkExa/LUguw6/c=
|
||||
github.com/metacubex/sing v0.5.6/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing-mux v0.3.4 h1:tf4r27CIkzaxq9kBlAXQkgMXq2HPp5Mta60Kb4RCZF0=
|
||||
github.com/metacubex/sing-mux v0.3.4/go.mod h1:SEJfAuykNj/ozbPqngEYqyggwSr81+L7Nu09NRD5mh4=
|
||||
github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb h1:gxrJmnxuEAel+kh3V7ntqkHjURif0xKDu76nzr/BF5Y=
|
||||
github.com/metacubex/sing-quic v0.0.0-20251004051927-c45ee18473bb/go.mod h1:JK4+PYUKps6pnlicKjsSUAjAcvIUjhorIjdNZGg930M=
|
||||
github.com/metacubex/sing v0.5.5 h1:m5U8iHvRAUxlme3FZlE/LPIGHjU8oMCUzXWGbQQAC1E=
|
||||
github.com/metacubex/sing v0.5.5/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac h1:wDH/Jh/yqWbzPktqJP+Y1cUG8hchcrzKzUxJiSpnaQs=
|
||||
github.com/metacubex/sing-mux v0.3.3-0.20250813083925-d7c9aeaeeaac/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb h1:U/m3h8lp/j7i8zFgfvScLdZa1/Y8dd74oO7iZaQq80s=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250718154553-1b193bec4cbb/go.mod h1:B60FxaPHjR1SeQB0IiLrgwgvKsaoASfOWdiqhLjmMGA=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.6 h1:ZR1kYT0f0Vi64iQSS09OdhFfppiNkh7kjgRdMm0SB98=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.6/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
|
||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
|
||||
github.com/metacubex/sing-tun v0.4.9 h1:jY0Yyt8nnN3yQRN/jTxgqNCmGi1dsFdxdIi7pQUlVVU=
|
||||
github.com/metacubex/sing-tun v0.4.9/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
|
||||
github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I=
|
||||
github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
|
||||
github.com/metacubex/sing-tun v0.4.7 h1:ZDY/W+1c7PeWWKeKRyUo18fySF/TWjB0i5ui81Ar778=
|
||||
github.com/metacubex/sing-tun v0.4.7/go.mod h1:xHecZRwBnKWe6zG9amAK9cXf91lF6blgjBqm+VvOrmU=
|
||||
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0 h1:WZepq4TOZa6WewB8tGAZrrL+bL2R2ivoBzuEgAeolWc=
|
||||
github.com/metacubex/sing-vmess v0.2.4-0.20250822020810-4856053566f0/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
|
||||
github.com/metacubex/smux v0.0.0-20250922175018-15c9a6a78719 h1:T6qCCfolRDAVJKeaPW/mXwNLjnlo65AYN7WS2jrBNaM=
|
||||
github.com/metacubex/smux v0.0.0-20250922175018-15c9a6a78719/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
|
||||
github.com/metacubex/tfo-go v0.0.0-20251024101424-368b42b59148 h1:Zd0QqciLIhv9MKbGKTPEgN8WUFsgQGA1WJBy6spEnVU=
|
||||
github.com/metacubex/tfo-go v0.0.0-20251024101424-368b42b59148/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
github.com/metacubex/utls v1.8.3 h1:0m/yCxm3SK6kWve2lKiFb1pue1wHitJ8sQQD4Ikqde4=
|
||||
github.com/metacubex/utls v1.8.3/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
|
||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo=
|
||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
|
||||
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617 h1:yN3mQ4cT9sPUciw/rO0Isc/8QlO86DB6g9SEMRgQ8Cw=
|
||||
github.com/metacubex/tfo-go v0.0.0-20250827083229-aa432b865617/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142 h1:csEbKOzRAxJXffOeZnnS3/kA/F55JiTbKv5jcYqCXms=
|
||||
github.com/metacubex/utls v1.8.1-0.20250823120917-12f5ba126142/go.mod h1:67I3skhEY4Sya8f1YxELwWPoeQdXqZCrWNYLvq8gn2U=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f/go.mod h1:oPGcV994OGJedmmxrcK9+ni7jUEMGhR+uVQAdaduIP4=
|
||||
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 h1:lhlqpYHopuTLx9xQt22kSA9HtnyTDmk5XjjQVCGHe2E=
|
||||
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49/go.mod h1:MBeEa9IVBphH7vc3LNtW6ZujVXFizotPo3OEiHQ+TNU=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
|
||||
@@ -164,14 +160,18 @@ github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
|
||||
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=
|
||||
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=
|
||||
@@ -191,7 +191,11 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
|
||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
@@ -205,12 +209,14 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiEYzdMlNsVvBYOAJWZjdktqFE9tQh5BT2+4=
|
||||
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs=
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
|
||||
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -234,13 +240,16 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
@@ -254,6 +263,7 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
20
core/hub.go
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/metacubex/mihomo/component/updater"
|
||||
"github.com/metacubex/mihomo/config"
|
||||
"github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/constant/features"
|
||||
cp "github.com/metacubex/mihomo/constant/provider"
|
||||
"github.com/metacubex/mihomo/hub/executor"
|
||||
"github.com/metacubex/mihomo/listener"
|
||||
@@ -22,7 +21,6 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -35,8 +33,6 @@ var (
|
||||
)
|
||||
|
||||
func handleInitClash(paramsString string) bool {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
var params = InitParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), ¶ms)
|
||||
if err != nil {
|
||||
@@ -73,24 +69,22 @@ func handleGetIsInit() bool {
|
||||
}
|
||||
|
||||
func handleForceGC() {
|
||||
log.Infoln("[APP] request force GC")
|
||||
runtime.GC()
|
||||
if features.Android {
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
go func() {
|
||||
log.Infoln("[APP] request force GC")
|
||||
runtime.GC()
|
||||
}()
|
||||
}
|
||||
|
||||
func handleShutdown() bool {
|
||||
stopListeners()
|
||||
executor.Shutdown()
|
||||
handleForceGC()
|
||||
runtime.GC()
|
||||
isInit = false
|
||||
return true
|
||||
}
|
||||
|
||||
func handleValidateConfig(path string) string {
|
||||
buf, err := readFile(path)
|
||||
_, err = config.UnmarshalRawConfig(buf)
|
||||
func handleValidateConfig(bytes []byte) string {
|
||||
_, err := config.UnmarshalRawConfig(bytes)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ type TunHandler struct {
|
||||
}
|
||||
|
||||
func (th *TunHandler) start(fd int, stack, address, dns string) {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
_ = th.limit.Acquire(context.TODO(), 4)
|
||||
defer th.limit.Release(4)
|
||||
th.initHook()
|
||||
|
||||
@@ -24,14 +24,15 @@ class Application extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class ApplicationState extends ConsumerState<Application> {
|
||||
Timer? _autoUpdateGroupTaskTimer;
|
||||
Timer? _autoUpdateProfilesTaskTimer;
|
||||
|
||||
final _pageTransitionsTheme = const PageTransitionsTheme(
|
||||
builders: <TargetPlatform, PageTransitionsBuilder>{
|
||||
TargetPlatform.android: commonSharedXPageTransitions,
|
||||
TargetPlatform.windows: commonSharedXPageTransitions,
|
||||
TargetPlatform.linux: commonSharedXPageTransitions,
|
||||
TargetPlatform.macOS: commonSharedXPageTransitions,
|
||||
TargetPlatform.android: CommonPageTransitionsBuilder(),
|
||||
TargetPlatform.windows: CommonPageTransitionsBuilder(),
|
||||
TargetPlatform.linux: CommonPageTransitionsBuilder(),
|
||||
TargetPlatform.macOS: CommonPageTransitionsBuilder(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -101,7 +102,7 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
}
|
||||
|
||||
Widget _buildApp({required Widget child}) {
|
||||
return StatusManager(child: ThemeManager(child: child));
|
||||
return MessageManager(child: ThemeManager(child: child));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -161,6 +162,7 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
linkManager.destroy();
|
||||
_autoUpdateGroupTaskTimer?.cancel();
|
||||
_autoUpdateProfilesTaskTimer?.cancel();
|
||||
await coreController.destroy();
|
||||
await globalState.appController.savePreferences();
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import 'package:fl_clash/l10n/l10n.dart';
|
||||
|
||||
final appLocalizations = AppLocalizations.current;
|
||||
final appLocalizations = AppLocalizations.current;
|
||||
@@ -7,9 +7,6 @@ import 'package:path/path.dart';
|
||||
extension ArchiveExt on Archive {
|
||||
void addDirectoryToArchive(String dirPath, String parentPath) {
|
||||
final dir = Directory(dirPath);
|
||||
if (!dir.existsSync()) {
|
||||
return;
|
||||
}
|
||||
final entities = dir.listSync(recursive: false);
|
||||
for (final entity in entities) {
|
||||
final relativePath = relative(entity.path, from: parentPath);
|
||||
|
||||
@@ -38,4 +38,3 @@ export 'text.dart';
|
||||
export 'tray.dart';
|
||||
export 'utils.dart';
|
||||
export 'window.dart';
|
||||
export 'yaml.dart';
|
||||
|
||||
@@ -7,7 +7,7 @@ List<Group> computeSort({
|
||||
required List<Group> groups,
|
||||
required ProxiesSortType sortType,
|
||||
required DelayMap delayMap,
|
||||
required Map<String, String> selectedMap,
|
||||
required SelectedMap selectedMap,
|
||||
required String defaultTestUrl,
|
||||
}) {
|
||||
return groups.map((group) {
|
||||
@@ -31,7 +31,7 @@ DelayState computeProxyDelayState({
|
||||
required String proxyName,
|
||||
required String testUrl,
|
||||
required List<Group> groups,
|
||||
required Map<String, String> selectedMap,
|
||||
required SelectedMap selectedMap,
|
||||
required DelayMap delayMap,
|
||||
}) {
|
||||
final state = computeRealSelectedProxyState(
|
||||
@@ -47,7 +47,7 @@ DelayState computeProxyDelayState({
|
||||
SelectedProxyState computeRealSelectedProxyState(
|
||||
String proxyName, {
|
||||
required List<Group> groups,
|
||||
required Map<String, String> selectedMap,
|
||||
required SelectedMap selectedMap,
|
||||
}) {
|
||||
return _getRealSelectedProxyState(
|
||||
SelectedProxyState(proxyName: proxyName),
|
||||
@@ -59,7 +59,7 @@ SelectedProxyState computeRealSelectedProxyState(
|
||||
SelectedProxyState _getRealSelectedProxyState(
|
||||
SelectedProxyState state, {
|
||||
required List<Group> groups,
|
||||
required Map<String, String> selectedMap,
|
||||
required SelectedMap selectedMap,
|
||||
}) {
|
||||
if (state.proxyName.isEmpty) return state;
|
||||
final index = groups.indexWhere((element) => element.name == state.proxyName);
|
||||
@@ -83,7 +83,7 @@ List<Proxy> _sortOfDelay({
|
||||
required List<Group> groups,
|
||||
required List<Proxy> proxies,
|
||||
required DelayMap delayMap,
|
||||
required Map<String, String> selectedMap,
|
||||
required SelectedMap selectedMap,
|
||||
required String testUrl,
|
||||
}) {
|
||||
return List.from(proxies)..sort((a, b) {
|
||||
|
||||
@@ -23,12 +23,6 @@ final baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||
vertical: 16.ap,
|
||||
horizontal: 16.ap,
|
||||
);
|
||||
final listHeaderPadding = EdgeInsets.only(
|
||||
left: 16.ap,
|
||||
right: 8.ap,
|
||||
top: 24.ap,
|
||||
bottom: 8.ap,
|
||||
);
|
||||
|
||||
final defaultTextScaleFactor =
|
||||
WidgetsBinding.instance.platformDispatcher.textScaleFactor;
|
||||
@@ -69,14 +63,10 @@ const stringListEquality = ListEquality<String>();
|
||||
const intListEquality = ListEquality<int>();
|
||||
const logListEquality = ListEquality<Log>();
|
||||
const groupListEquality = ListEquality<Group>();
|
||||
const ruleListEquality = ListEquality<Rule>();
|
||||
const scriptEquality = ListEquality<Script>();
|
||||
const externalProviderListEquality = ListEquality<ExternalProvider>();
|
||||
const packageListEquality = ListEquality<Package>();
|
||||
const hotKeyActionListEquality = ListEquality<HotKeyAction>();
|
||||
const stringAndStringMapEquality = MapEquality<String, String>();
|
||||
const stringAndStringMapEntryListEquality =
|
||||
ListEquality<MapEntry<String, String>>();
|
||||
const stringAndStringMapEntryIterableEquality =
|
||||
IterableEquality<MapEntry<String, String>>();
|
||||
const delayMapEquality = MapEquality<String, Map<String, int?>>();
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import 'package:fl_clash/l10n/l10n.dart';
|
||||
import 'package:fl_clash/manager/manager.dart';
|
||||
import 'package:fl_clash/models/widget.dart';
|
||||
import 'package:fl_clash/manager/message_manager.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -9,11 +7,8 @@ extension BuildContextExtension on BuildContext {
|
||||
return findAncestorStateOfType<CommonScaffoldState>();
|
||||
}
|
||||
|
||||
void showNotifier(String text, {MessageActionState? actionState}) {
|
||||
return findAncestorStateOfType<StatusManagerState>()?.message(
|
||||
text,
|
||||
actionState: actionState,
|
||||
);
|
||||
void showNotifier(String text) {
|
||||
return findAncestorStateOfType<MessageManagerState>()?.message(text);
|
||||
}
|
||||
|
||||
void showSnackBar(String message, {SnackBarAction? action}) {
|
||||
@@ -47,8 +42,6 @@ extension BuildContextExtension on BuildContext {
|
||||
|
||||
TextTheme get textTheme => Theme.of(this).textTheme;
|
||||
|
||||
AppLocalizations get appLocalizations => AppLocalizations.of(this);
|
||||
|
||||
T? findLastStateOfType<T extends State>() {
|
||||
T? state;
|
||||
|
||||
|
||||
@@ -17,25 +17,23 @@ extension DateTimeExtension on DateTime {
|
||||
final difference = currentDateTime.difference(this);
|
||||
final days = difference.inDays;
|
||||
if (days >= 365) {
|
||||
final years = (days / 365).floor();
|
||||
return appLocalizations.yearsAgo(years);
|
||||
return '${(days / 365).floor()} ${appLocalizations.years}${appLocalizations.ago}';
|
||||
}
|
||||
if (days >= 30) {
|
||||
final months = (days / 30).floor();
|
||||
return appLocalizations.monthsAgo(months);
|
||||
return '${(days / 30).floor()} ${appLocalizations.months}${appLocalizations.ago}';
|
||||
}
|
||||
if (days >= 1) {
|
||||
return appLocalizations.daysAgo(days);
|
||||
return '$days ${appLocalizations.days}${appLocalizations.ago}';
|
||||
}
|
||||
final hours = difference.inHours;
|
||||
if (hours >= 1) {
|
||||
return appLocalizations.hoursAgo(hours);
|
||||
return '$hours ${appLocalizations.hours}${appLocalizations.ago}';
|
||||
}
|
||||
final minutes = difference.inMinutes;
|
||||
if (minutes >= 1) {
|
||||
return appLocalizations.minutesAgo(minutes);
|
||||
return '$minutes ${appLocalizations.minutes}${appLocalizations.ago}';
|
||||
}
|
||||
return appLocalizations.justNow;
|
||||
return appLocalizations.just;
|
||||
}
|
||||
|
||||
String get show {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
|
||||
class Debouncer {
|
||||
@@ -37,25 +36,16 @@ class Throttler {
|
||||
Function func, {
|
||||
List<dynamic>? args,
|
||||
Duration duration = const Duration(milliseconds: 600),
|
||||
bool fire = false,
|
||||
}) {
|
||||
final timer = _operations[tag];
|
||||
if (timer != null) {
|
||||
return true;
|
||||
}
|
||||
if (fire) {
|
||||
_operations[tag] = Timer(duration, () {
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
Function.apply(func, args);
|
||||
_operations[tag] = Timer(duration, () {
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
});
|
||||
} else {
|
||||
_operations[tag] = Timer(duration, () {
|
||||
Function.apply(func, args);
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -69,7 +59,7 @@ Future<T> retry<T>({
|
||||
required Future<T> Function() task,
|
||||
int maxAttempts = 3,
|
||||
required bool Function(T res) retryIf,
|
||||
Duration delay = midDuration,
|
||||
Duration delay = Duration.zero,
|
||||
}) async {
|
||||
int attempts = 0;
|
||||
while (attempts < maxAttempts) {
|
||||
@@ -79,7 +69,7 @@ Future<T> retry<T>({
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
throw 'retry error';
|
||||
throw 'unknown error';
|
||||
}
|
||||
|
||||
final debouncer = Debouncer();
|
||||
|
||||
@@ -10,14 +10,14 @@ extension FutureExt<T> on Future<T> {
|
||||
VoidCallback? onLast,
|
||||
FutureOr<T> Function()? onTimeout,
|
||||
}) {
|
||||
final realTimeout = timeout ?? const Duration(minutes: 3);
|
||||
Timer(realTimeout + commonDuration, () {
|
||||
final realTimout = timeout ?? const Duration(minutes: 3);
|
||||
Timer(realTimout + commonDuration, () {
|
||||
if (onLast != null) {
|
||||
onLast();
|
||||
}
|
||||
});
|
||||
return this.timeout(
|
||||
realTimeout,
|
||||
realTimout,
|
||||
onTimeout: () async {
|
||||
if (onTimeout != null) {
|
||||
return onTimeout();
|
||||
|
||||
@@ -23,7 +23,10 @@ extension IterableExt<T> on Iterable<T> {
|
||||
}
|
||||
}
|
||||
|
||||
Iterable<T> fill(int length, {required T Function(int count) filler}) sync* {
|
||||
Iterable<T> fill(
|
||||
int length, {
|
||||
required T Function(int count) filler,
|
||||
}) sync* {
|
||||
int count = 0;
|
||||
for (var item in this) {
|
||||
yield item;
|
||||
@@ -82,31 +85,6 @@ extension ListExt<T> on List<T> {
|
||||
if (length > index) return this[index];
|
||||
return last;
|
||||
}
|
||||
|
||||
T safeLast(T value) {
|
||||
if (isNotEmpty) {
|
||||
return last;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void addOrRemove(T value) {
|
||||
if (contains(value)) {
|
||||
remove(value);
|
||||
} else {
|
||||
add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SetExt<T> on Set<T> {
|
||||
void addOrRemove(T value) {
|
||||
if (contains(value)) {
|
||||
remove(value);
|
||||
} else {
|
||||
add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DoubleListExt on List<double> {
|
||||
@@ -142,14 +120,4 @@ extension MapExt<K, V> on Map<K, V> {
|
||||
}
|
||||
return this[key]!;
|
||||
}
|
||||
|
||||
Map<K, V> copyWitUpdate(K key, V? value) {
|
||||
final newMap = Map<K, V>.from(this);
|
||||
if (value == null) {
|
||||
newMap.remove(key);
|
||||
} else {
|
||||
newMap[key] = value;
|
||||
}
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,26 @@ class Measure {
|
||||
final Map<String, dynamic> _measureMap;
|
||||
|
||||
Measure.of(this.context, double textScaleFactor)
|
||||
: _measureMap = {},
|
||||
_textScaler = TextScaler.linear(textScaleFactor);
|
||||
: _measureMap = {},
|
||||
_textScaler = TextScaler.linear(
|
||||
textScaleFactor,
|
||||
);
|
||||
|
||||
Size computeTextSize(Text text, {double maxWidth = double.infinity}) {
|
||||
Size computeTextSize(
|
||||
Text text, {
|
||||
double maxWidth = double.infinity,
|
||||
}) {
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(text: text.data, style: text.style),
|
||||
text: TextSpan(
|
||||
text: text.data,
|
||||
style: text.style,
|
||||
),
|
||||
maxLines: text.maxLines,
|
||||
textScaler: _textScaler,
|
||||
textDirection: text.textDirection ?? TextDirection.ltr,
|
||||
)..layout(maxWidth: maxWidth);
|
||||
)..layout(
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
return textPainter.size;
|
||||
}
|
||||
|
||||
@@ -25,7 +35,10 @@ class Measure {
|
||||
return _measureMap.updateCacheValue(
|
||||
'bodyMediumHeight',
|
||||
() => computeTextSize(
|
||||
Text('X', style: context.textTheme.bodyMedium),
|
||||
Text(
|
||||
'X',
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
@@ -33,16 +46,24 @@ class Measure {
|
||||
double get bodyLargeHeight {
|
||||
return _measureMap.updateCacheValue(
|
||||
'bodyLargeHeight',
|
||||
() =>
|
||||
computeTextSize(Text('X', style: context.textTheme.bodyLarge)).height,
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
'X',
|
||||
style: context.textTheme.bodyLarge,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get bodySmallHeight {
|
||||
return _measureMap.updateCacheValue(
|
||||
'bodySmallHeight',
|
||||
() =>
|
||||
computeTextSize(Text('X', style: context.textTheme.bodySmall)).height,
|
||||
() => computeTextSize(
|
||||
Text(
|
||||
'X',
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -50,16 +71,10 @@ class Measure {
|
||||
return _measureMap.updateCacheValue(
|
||||
'labelSmallHeight',
|
||||
() => computeTextSize(
|
||||
Text('X', style: context.textTheme.labelSmall),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
double get titleSmallHeight {
|
||||
return _measureMap.updateCacheValue(
|
||||
'titleSmallHeight',
|
||||
() => computeTextSize(
|
||||
Text('X', style: context.textTheme.titleSmall),
|
||||
Text(
|
||||
'X',
|
||||
style: context.textTheme.labelSmall,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
@@ -68,7 +83,10 @@ class Measure {
|
||||
return _measureMap.updateCacheValue(
|
||||
'labelMediumHeight',
|
||||
() => computeTextSize(
|
||||
Text('X', style: context.textTheme.labelMedium),
|
||||
Text(
|
||||
'X',
|
||||
style: context.textTheme.labelMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
@@ -77,7 +95,10 @@ class Measure {
|
||||
return _measureMap.updateCacheValue(
|
||||
'titleLargeHeight',
|
||||
() => computeTextSize(
|
||||
Text('X', style: context.textTheme.titleLarge),
|
||||
Text(
|
||||
'X',
|
||||
style: context.textTheme.titleLarge,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
@@ -86,7 +107,10 @@ class Measure {
|
||||
return _measureMap.updateCacheValue(
|
||||
'titleMediumHeight',
|
||||
() => computeTextSize(
|
||||
Text('X', style: context.textTheme.titleMedium),
|
||||
Text(
|
||||
'X',
|
||||
style: context.textTheme.titleMedium,
|
||||
),
|
||||
).height,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,6 @@ mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
|
||||
}
|
||||
|
||||
void onUpdate(T value) {}
|
||||
|
||||
void update(T Function(T) builder) {
|
||||
final value = builder(state);
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
mixin AnyNotifierMixin<T> on AnyNotifier<T, T> {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
@@ -33,11 +32,6 @@ class BaseNavigator {
|
||||
// }
|
||||
}
|
||||
|
||||
const commonSharedXPageTransitions = SharedAxisPageTransitionsBuilder(
|
||||
transitionType: SharedAxisTransitionType.horizontal,
|
||||
fillColor: Colors.transparent,
|
||||
);
|
||||
|
||||
class CommonDesktopRoute<T> extends PageRoute<T> {
|
||||
final Widget Function(BuildContext context) builder;
|
||||
|
||||
@@ -73,45 +67,14 @@ class CommonDesktopRoute<T> extends PageRoute<T> {
|
||||
Duration get reverseTransitionDuration => Duration(milliseconds: 200);
|
||||
}
|
||||
|
||||
class CommonRoute<T> extends PageRoute<T> {
|
||||
final Widget Function(BuildContext context) builder;
|
||||
|
||||
CommonRoute({required this.builder});
|
||||
class CommonRoute<T> extends MaterialPageRoute<T> {
|
||||
CommonRoute({required super.builder});
|
||||
|
||||
@override
|
||||
Color? get barrierColor => null;
|
||||
Duration get transitionDuration => const Duration(milliseconds: 500);
|
||||
|
||||
@override
|
||||
String? get barrierLabel => null;
|
||||
|
||||
@override
|
||||
bool get maintainState => true;
|
||||
|
||||
@override
|
||||
Widget buildPage(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) {
|
||||
final Widget result = builder(context);
|
||||
return Semantics(
|
||||
scopesRoute: true,
|
||||
explicitChildNodes: true,
|
||||
child: SharedAxisTransition(
|
||||
animation: animation,
|
||||
secondaryAnimation: secondaryAnimation,
|
||||
transitionType: SharedAxisTransitionType.horizontal,
|
||||
fillColor: context.colorScheme.surface,
|
||||
child: result,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => Duration(milliseconds: 300);
|
||||
|
||||
@override
|
||||
Duration get reverseTransitionDuration => Duration(milliseconds: 300);
|
||||
Duration get reverseTransitionDuration => const Duration(milliseconds: 500);
|
||||
}
|
||||
|
||||
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
|
||||
@@ -171,15 +134,11 @@ class CommonPageTransition extends StatefulWidget {
|
||||
bool allowSnapshotting,
|
||||
Widget? child,
|
||||
) {
|
||||
final CurvedAnimation animation = CurvedAnimation(
|
||||
final Animation<Offset> delegatedPositionAnimation = CurvedAnimation(
|
||||
parent: secondaryAnimation,
|
||||
curve: Curves.linearToEaseOut,
|
||||
reverseCurve: Curves.easeInToLinear,
|
||||
);
|
||||
final Animation<Offset> delegatedPositionAnimation = animation.drive(
|
||||
_kMiddleLeftTween,
|
||||
);
|
||||
animation.dispose();
|
||||
).drive(_kMiddleLeftTween);
|
||||
|
||||
assert(debugCheckHasDirectionality(context));
|
||||
final TextDirection textDirection = Directionality.of(context);
|
||||
@@ -265,7 +224,7 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
|
||||
DecorationTween(
|
||||
begin: const _CommonEdgeShadowDecoration(),
|
||||
end: _CommonEdgeShadowDecoration(<Color>[
|
||||
Color(0x04000000),
|
||||
widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02),
|
||||
Colors.transparent,
|
||||
]),
|
||||
),
|
||||
@@ -316,7 +275,7 @@ class _CommonEdgeShadowPainter extends BoxPainter {
|
||||
return;
|
||||
}
|
||||
|
||||
final double shadowWidth = 0.05 * configuration.size!.width;
|
||||
final double shadowWidth = 1 * configuration.size!.width;
|
||||
final double shadowHeight = configuration.size!.height;
|
||||
final double bandWidth = shadowWidth / (colors.length - 1);
|
||||
|
||||
|
||||
@@ -33,25 +33,11 @@ extension NumExt on num {
|
||||
unit: units[unitIndex].name,
|
||||
);
|
||||
}
|
||||
|
||||
TrafficShow get shortTraffic {
|
||||
final units = TrafficUnit.values;
|
||||
var size = toDouble();
|
||||
var unitIndex = 0;
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
return TrafficShow(
|
||||
value: size.toStringAsFixed(0),
|
||||
unit: ' ${units[unitIndex].name}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension DoubleExt on double {
|
||||
bool moreOrEqual(double value) {
|
||||
return this > value || (value - this).abs() < precisionErrorTolerance + 2;
|
||||
return this > value || (value - this).abs() < precisionErrorTolerance + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,12 +68,7 @@ class AppPath {
|
||||
|
||||
Future<String> get configFilePath async {
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
return join(homeDirPath, 'config.yaml');
|
||||
}
|
||||
|
||||
Future<String> get validateFilePath async {
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
return join(homeDirPath, 'temp', 'validate${utils.id}.yaml');
|
||||
return join(homeDirPath, 'config.json');
|
||||
}
|
||||
|
||||
Future<String> get sharedPreferencesPath async {
|
||||
|
||||
@@ -42,7 +42,8 @@ class Preferences {
|
||||
|
||||
Future<bool> saveConfig(Config config) async {
|
||||
final preferences = await sharedPreferencesCompleter.future;
|
||||
return preferences?.setString(configKey, json.encode(config)) ?? false;
|
||||
return await preferences?.setString(configKey, json.encode(config)) ??
|
||||
false;
|
||||
}
|
||||
|
||||
Future<void> clearClashConfig() async {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -13,14 +12,14 @@ class CommonPrint {
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
void log(String? text, {LogLevel logLevel = LogLevel.info}) {
|
||||
void log(String? text) {
|
||||
final payload = '[APP] $text';
|
||||
debugPrint(payload);
|
||||
if (!globalState.isInit) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.addLog(
|
||||
Log.app(payload).copyWith(logLevel: logLevel),
|
||||
Log.app(payload),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +30,16 @@ class Request {
|
||||
);
|
||||
}
|
||||
|
||||
Future<Response<Uint8List>> getFileResponseForUrl(String url) async {
|
||||
return await _clashDio.get<Uint8List>(
|
||||
Future<Response> getFileResponseForUrl(String url) async {
|
||||
final response = await _clashDio.get(
|
||||
url,
|
||||
options: Options(responseType: ResponseType.bytes),
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Response<String>> getTextResponseForUrl(String url) async {
|
||||
final response = await _clashDio.get<String>(
|
||||
Future<Response> getTextResponseForUrl(String url) async {
|
||||
final response = await _clashDio.get(
|
||||
url,
|
||||
options: Options(responseType: ResponseType.plain),
|
||||
);
|
||||
@@ -72,13 +73,13 @@ class Request {
|
||||
}
|
||||
|
||||
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
|
||||
'https://ipwho.is': IpInfo.fromIpWhoIsJson,
|
||||
'https://api.myip.com': IpInfo.fromMyIpJson,
|
||||
'https://ipapi.co/json': IpInfo.fromIpApiCoJson,
|
||||
'https://ident.me/json': IpInfo.fromIdentMeJson,
|
||||
'http://ip-api.com/json': IpInfo.fromIpAPIJson,
|
||||
'https://api.ip.sb/geoip': IpInfo.fromIpSbJson,
|
||||
'https://ipinfo.io/json': IpInfo.fromIpInfoIoJson,
|
||||
'https://ipwho.is/': IpInfo.fromIpWhoIsJson,
|
||||
'https://api.myip.com/': IpInfo.fromMyIpJson,
|
||||
'https://ipapi.co/json/': IpInfo.fromIpApiCoJson,
|
||||
'https://ident.me/json/': IpInfo.fromIdentMeJson,
|
||||
'http://ip-api.com/json/': IpInfo.fromIpAPIJson,
|
||||
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
|
||||
'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson,
|
||||
};
|
||||
|
||||
Future<Result<IpInfo?>> checkIp({CancelToken? cancelToken}) async {
|
||||
@@ -91,13 +92,11 @@ class Request {
|
||||
}
|
||||
}
|
||||
|
||||
final future = dio
|
||||
.get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
)
|
||||
.timeout(const Duration(seconds: 10));
|
||||
final future = dio.get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
future
|
||||
.then((res) {
|
||||
if (res.statusCode == HttpStatus.ok && res.data != null) {
|
||||
|
||||
@@ -11,25 +11,16 @@ extension StringExtension on String {
|
||||
}
|
||||
|
||||
dynamic get splitByMultipleSeparators {
|
||||
final parts = split(
|
||||
RegExp(r'[, ;]+'),
|
||||
).where((part) => part.isNotEmpty).toList();
|
||||
final parts =
|
||||
split(RegExp(r'[, ;]+')).where((part) => part.isNotEmpty).toList();
|
||||
|
||||
return parts.length > 1 ? parts : this;
|
||||
}
|
||||
|
||||
int compareToLower(String other) {
|
||||
return toLowerCase().compareTo(other.toLowerCase());
|
||||
}
|
||||
|
||||
String safeSubstring(int start, [int? end]) {
|
||||
if (isEmpty) return '';
|
||||
final safeStart = start.clamp(0, length);
|
||||
if (end == null) {
|
||||
return substring(safeStart);
|
||||
}
|
||||
final safeEnd = end.clamp(safeStart, length);
|
||||
return substring(safeStart, safeEnd);
|
||||
return toLowerCase().compareTo(
|
||||
other.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
List<int> get encodeUtf16LeWithBom {
|
||||
@@ -75,9 +66,9 @@ extension StringExtension on String {
|
||||
return md5.convert(bytes).toString();
|
||||
}
|
||||
|
||||
// bool containsToLower(String target) {
|
||||
// return toLowerCase().contains(target);
|
||||
// }
|
||||
// bool containsToLower(String target) {
|
||||
// return toLowerCase().contains(target);
|
||||
// }
|
||||
}
|
||||
|
||||
extension StringExtensionSafe on String? {
|
||||
|
||||
@@ -181,31 +181,28 @@ class Windows {
|
||||
calloc.free(argumentsPtr);
|
||||
calloc.free(operationPtr);
|
||||
|
||||
commonPrint.log(
|
||||
'windows runas: $command $arguments resultCode:$result',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
commonPrint.log('windows runas: $command $arguments resultCode:$result');
|
||||
|
||||
if (result <= 32) {
|
||||
if (result < 42) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Future<void> _killProcess(int port) async {
|
||||
// final result = await Process.run('netstat', ['-ano']);
|
||||
// final lines = result.stdout.toString().trim().split('\n');
|
||||
// for (final line in lines) {
|
||||
// if (!line.contains(':$port') || !line.contains('LISTENING')) {
|
||||
// continue;
|
||||
// }
|
||||
// final parts = line.trim().split(RegExp(r'\s+'));
|
||||
// final pid = int.tryParse(parts.last);
|
||||
// if (pid != null) {
|
||||
// await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
Future<void> _killProcess(int port) async {
|
||||
final result = await Process.run('netstat', ['-ano']);
|
||||
final lines = result.stdout.toString().trim().split('\n');
|
||||
for (final line in lines) {
|
||||
if (!line.contains(':$port') || !line.contains('LISTENING')) {
|
||||
continue;
|
||||
}
|
||||
final parts = line.trim().split(RegExp(r'\s+'));
|
||||
final pid = int.tryParse(parts.last);
|
||||
if (pid != null) {
|
||||
await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<WindowsHelperServiceStatus> checkService() async {
|
||||
// final qcResult = await Process.run('sc', ['qc', appHelperService]);
|
||||
@@ -231,18 +228,16 @@ class Windows {
|
||||
return true;
|
||||
}
|
||||
|
||||
await _killProcess(helperPort);
|
||||
|
||||
final command = [
|
||||
'/c',
|
||||
if (status == WindowsHelperServiceStatus.presence) ...[
|
||||
'taskkill',
|
||||
'/F',
|
||||
'/IM',
|
||||
'$appHelperService.exe'
|
||||
' & '
|
||||
'sc',
|
||||
'sc',
|
||||
'delete',
|
||||
appHelperService,
|
||||
'&',
|
||||
'/force',
|
||||
'&&',
|
||||
],
|
||||
'sc',
|
||||
'create',
|
||||
@@ -258,12 +253,8 @@ class Windows {
|
||||
final res = runas('cmd.exe', command);
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
final retryStatus = await retry(
|
||||
task: checkService,
|
||||
retryIf: (status) => status != WindowsHelperServiceStatus.running,
|
||||
delay: commonDuration,
|
||||
);
|
||||
return res && retryStatus == WindowsHelperServiceStatus.running;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<bool> registerTask(String appName) async {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/iterable.dart';
|
||||
import 'package:fl_clash/common/utils.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/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
@@ -14,34 +15,24 @@ import 'system.dart';
|
||||
import 'window.dart';
|
||||
|
||||
class Tray {
|
||||
String get trayIconSuffix {
|
||||
return system.isWindows ? 'ico' : 'png';
|
||||
}
|
||||
|
||||
String getTryIcon({required bool isStart, required bool tunEnable}) {
|
||||
if (system.isMacOS || !isStart) {
|
||||
return 'assets/images/icon/status_1.$trayIconSuffix';
|
||||
}
|
||||
if (!tunEnable) {
|
||||
return 'assets/images/icon/status_2.$trayIconSuffix';
|
||||
}
|
||||
return 'assets/images/icon/status_3.$trayIconSuffix';
|
||||
}
|
||||
|
||||
Future _updateSystemTray({
|
||||
required Brightness? brightness,
|
||||
bool force = false,
|
||||
required bool isStart,
|
||||
required bool tunEnable,
|
||||
}) async {
|
||||
if (Platform.isLinux || force) {
|
||||
await trayManager.destroy();
|
||||
}
|
||||
await trayManager.setIcon(
|
||||
getTryIcon(isStart: isStart, tunEnable: tunEnable),
|
||||
utils.getTrayIconPath(
|
||||
brightness: brightness ??
|
||||
WidgetsBinding.instance.platformDispatcher.platformBrightness,
|
||||
),
|
||||
isTemplate: true,
|
||||
);
|
||||
if (!Platform.isLinux) {
|
||||
await trayManager.setToolTip(appName);
|
||||
await trayManager.setToolTip(
|
||||
appName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,10 +43,9 @@ class Tray {
|
||||
if (system.isAndroid) {
|
||||
return;
|
||||
}
|
||||
if (!system.isLinux) {
|
||||
if (!Platform.isLinux) {
|
||||
await _updateSystemTray(
|
||||
isStart: trayState.isStart,
|
||||
tunEnable: trayState.tunEnable,
|
||||
brightness: trayState.brightness,
|
||||
force: focus,
|
||||
);
|
||||
}
|
||||
@@ -75,16 +65,6 @@ class Tray {
|
||||
checked: false,
|
||||
);
|
||||
menuItems.add(startMenuItem);
|
||||
if (system.isMacOS) {
|
||||
final speedStatistics = MenuItem.checkbox(
|
||||
label: appLocalizations.speedStatistics,
|
||||
onClick: (_) async {
|
||||
globalState.appController.updateSpeedStatistics();
|
||||
},
|
||||
checked: trayState.showTrayTitle,
|
||||
);
|
||||
menuItems.add(speedStatistics);
|
||||
}
|
||||
menuItems.add(MenuItem.separator());
|
||||
for (final mode in Mode.values) {
|
||||
menuItems.add(
|
||||
@@ -105,11 +85,13 @@ class Tray {
|
||||
subMenuItems.add(
|
||||
MenuItem.checkbox(
|
||||
label: proxy.name,
|
||||
checked:
|
||||
globalState.getSelectedProxyName(group.name) == proxy.name,
|
||||
checked: trayState.selectedMap[group.name] == proxy.name,
|
||||
onClick: (_) {
|
||||
final appController = globalState.appController;
|
||||
appController.updateCurrentSelectedMap(group.name, proxy.name);
|
||||
appController.updateCurrentSelectedMap(
|
||||
group.name,
|
||||
proxy.name,
|
||||
);
|
||||
appController.changeProxy(
|
||||
groupName: group.name,
|
||||
proxyName: proxy.name,
|
||||
@@ -121,7 +103,9 @@ class Tray {
|
||||
menuItems.add(
|
||||
MenuItem.submenu(
|
||||
label: group.name,
|
||||
submenu: Menu(items: subMenuItems),
|
||||
submenu: Menu(
|
||||
items: subMenuItems,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -175,41 +159,38 @@ class Tray {
|
||||
menuItems.add(exitMenuItem);
|
||||
final menu = Menu(items: menuItems);
|
||||
await trayManager.setContextMenu(menu);
|
||||
if (system.isLinux) {
|
||||
if (Platform.isLinux) {
|
||||
await _updateSystemTray(
|
||||
isStart: trayState.isStart,
|
||||
tunEnable: trayState.tunEnable,
|
||||
brightness: trayState.brightness,
|
||||
force: focus,
|
||||
);
|
||||
}
|
||||
updateTrayTitle(
|
||||
showTrayTitle: trayState.showTrayTitle,
|
||||
traffic: globalState.appState.traffics.list.safeLast(Traffic()),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateTrayTitle({
|
||||
required bool showTrayTitle,
|
||||
required Traffic traffic,
|
||||
}) async {
|
||||
if (!system.isMacOS) {
|
||||
return;
|
||||
}
|
||||
if (!showTrayTitle) {
|
||||
await trayManager.setTitle('');
|
||||
} else {
|
||||
await trayManager.setTitle(traffic.trayTitle);
|
||||
}
|
||||
Future<void> updateTrayTitle([Traffic? traffic]) async {
|
||||
// if (!system.isMacOS) {
|
||||
// return;
|
||||
// }
|
||||
// if (traffic == null) {
|
||||
// await trayManager.setTitle("");
|
||||
// } else {
|
||||
// await trayManager.setTitle(
|
||||
// "${traffic.up.shortShow} ↑ \n${traffic.down.shortShow} ↓",
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
Future<void> _copyEnv(int port) async {
|
||||
final url = 'http://127.0.0.1:$port';
|
||||
|
||||
final cmdline = system.isWindows
|
||||
? 'set \$env:all_proxy=$url'
|
||||
: 'export all_proxy=$url';
|
||||
final cmdline =
|
||||
system.isWindows ? 'set \$env:all_proxy=$url' : 'export all_proxy=$url';
|
||||
|
||||
await Clipboard.setData(ClipboardData(text: cmdline));
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: cmdline,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,9 +142,16 @@ class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
String get traySuffix {
|
||||
String getTrayIconPath({required Brightness brightness}) {
|
||||
if (system.isMacOS) {
|
||||
return 'assets/images/icon_white.png';
|
||||
}
|
||||
final suffix = system.isWindows ? 'ico' : 'png';
|
||||
return 'assets/images/icon/status_2.$suffix';
|
||||
return 'assets/images/icon.$suffix';
|
||||
// return switch (brightness) {
|
||||
// Brightness.dark => "assets/images/icon_white.$suffix",
|
||||
// Brightness.light => "assets/images/icon_black.$suffix",
|
||||
// };
|
||||
}
|
||||
|
||||
int compareVersions(String version1, String version2) {
|
||||
@@ -315,15 +322,12 @@ class Utils {
|
||||
return SingleActivator(trigger, control: control, meta: !control);
|
||||
}
|
||||
|
||||
FutureOr<T> handleWatch<T>({
|
||||
required Function function,
|
||||
required void Function(T data, int elapsedMilliseconds) onWatch,
|
||||
}) async {
|
||||
FutureOr<T> handleWatch<T>(Function function) async {
|
||||
if (kDebugMode) {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final res = await function();
|
||||
stopwatch.stop();
|
||||
onWatch(res, stopwatch.elapsedMilliseconds);
|
||||
commonPrint.log('耗时:${stopwatch.elapsedMilliseconds} ms');
|
||||
return res;
|
||||
}
|
||||
return await function();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:screen_retriever/screen_retriever.dart';
|
||||
@@ -20,22 +19,13 @@ class Window {
|
||||
protocol.register('flclash');
|
||||
}
|
||||
await windowManager.ensureInitialized();
|
||||
// kDebugMode ? Size(680, 580) :
|
||||
WindowOptions windowOptions = WindowOptions(
|
||||
size: props.size,
|
||||
size: Size(props.width, props.height),
|
||||
minimumSize: const Size(380, 400),
|
||||
);
|
||||
if (!system.isMacOS || version > 10) {
|
||||
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
|
||||
}
|
||||
await windowManager.setMaximizable(false);
|
||||
await _windowPosition(props);
|
||||
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await windowManager.setPreventClose(true);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _windowPosition(WindowProps props) async {
|
||||
if (!system.isMacOS) {
|
||||
final left = props.left ?? 0;
|
||||
final top = props.top ?? 0;
|
||||
@@ -60,6 +50,9 @@ class Window {
|
||||
}
|
||||
}
|
||||
}
|
||||
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await windowManager.setPreventClose(true);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> show() async {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:yaml_writer/yaml_writer.dart';
|
||||
|
||||
class Yaml {
|
||||
static Yaml? _instance;
|
||||
|
||||
Yaml._internal();
|
||||
|
||||
factory Yaml() {
|
||||
_instance ??= Yaml._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
String encode(Object? value) {
|
||||
return YamlWriter().convert(value);
|
||||
}
|
||||
}
|
||||
|
||||
final yaml = Yaml();
|
||||
@@ -22,6 +22,8 @@ import 'common/common.dart';
|
||||
import 'models/models.dart';
|
||||
|
||||
class AppController {
|
||||
int? lastProfileModified;
|
||||
|
||||
final BuildContext context;
|
||||
final WidgetRef _ref;
|
||||
|
||||
@@ -56,11 +58,7 @@ class AppController {
|
||||
}
|
||||
|
||||
void savePreferencesDebounce() {
|
||||
debouncer.call(
|
||||
FunctionTag.savePreferences,
|
||||
savePreferences,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
debouncer.call(FunctionTag.savePreferences, savePreferences);
|
||||
}
|
||||
|
||||
void changeProxyDebounce(String groupName, String proxyName) {
|
||||
@@ -69,7 +67,7 @@ class AppController {
|
||||
String proxyName,
|
||||
) async {
|
||||
await changeProxy(groupName: groupName, proxyName: proxyName);
|
||||
updateGroupsDebounce();
|
||||
await updateGroups();
|
||||
}, args: [groupName, proxyName]);
|
||||
}
|
||||
|
||||
@@ -84,26 +82,17 @@ class AppController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> tryStartCore() async {
|
||||
if (coreController.isCompleted) {
|
||||
return;
|
||||
}
|
||||
globalState.isUserDisconnected = true;
|
||||
await _connectCore();
|
||||
await _initCore();
|
||||
_ref.read(initProvider.notifier).value = true;
|
||||
if (_ref.read(isStartProvider)) {
|
||||
await globalState.handleStart();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateStatus(bool isStart) async {
|
||||
if (isStart) {
|
||||
await globalState.appController.tryStartCore();
|
||||
await globalState.handleStart([updateRunTime, updateTraffic]);
|
||||
final profileId = _ref.read(currentProfileIdProvider);
|
||||
final setupState = globalState.getSetupState(profileId);
|
||||
if (!setupState.needSetup(globalState.lastSetupState)) {
|
||||
final currentLastModified = await _ref
|
||||
.read(currentProfileProvider)
|
||||
?.profileLastModified;
|
||||
if (currentLastModified == null || lastProfileModified == null) {
|
||||
addCheckIpNumDebounce();
|
||||
return;
|
||||
}
|
||||
if (currentLastModified <= (lastProfileModified ?? 0)) {
|
||||
addCheckIpNumDebounce();
|
||||
return;
|
||||
}
|
||||
@@ -310,15 +299,9 @@ class AppController {
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable);
|
||||
final currentProfile = _ref.read(currentProfileProvider);
|
||||
final setupState = _ref.read(setupStateProvider(currentProfile?.id ?? ''));
|
||||
globalState.lastSetupState = setupState;
|
||||
if (system.isAndroid) {
|
||||
globalState.lastVpnState = _ref.read(vpnStateProvider);
|
||||
}
|
||||
final message = await globalState.setupConfig(
|
||||
setupState: setupState,
|
||||
patchConfig: realPatchConfig,
|
||||
final message = await coreController.setupConfig(realPatchConfig);
|
||||
lastProfileModified = await _ref.read(
|
||||
currentProfileProvider.select((state) => state?.profileLastModified),
|
||||
);
|
||||
if (message.isNotEmpty) {
|
||||
throw message;
|
||||
@@ -326,6 +309,7 @@ class AppController {
|
||||
}
|
||||
|
||||
Future _applyProfile() async {
|
||||
await coreController.requestGc();
|
||||
await setupClashConfig();
|
||||
await updateGroups();
|
||||
await updateProviders();
|
||||
@@ -369,14 +353,13 @@ class AppController {
|
||||
try {
|
||||
await updateProfile(profile);
|
||||
} catch (e) {
|
||||
commonPrint.log(e.toString(), logLevel: LogLevel.warning);
|
||||
commonPrint.log(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateGroups() async {
|
||||
try {
|
||||
commonPrint.log('updateGroups');
|
||||
_ref.read(groupsProvider.notifier).value = await retry(
|
||||
task: () async {
|
||||
final sortType = _ref.read(
|
||||
@@ -453,15 +436,15 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> handleExit() async {
|
||||
Future.delayed(Duration(seconds: 3), () {
|
||||
Future.delayed(commonDuration, () {
|
||||
system.exit();
|
||||
});
|
||||
try {
|
||||
await savePreferences();
|
||||
await proxy?.stopProxy();
|
||||
await macOS?.updateDns(true);
|
||||
await proxy?.stopProxy();
|
||||
await coreController.shutdown();
|
||||
await coreController.destroy();
|
||||
commonPrint.log('exit');
|
||||
} finally {
|
||||
system.exit();
|
||||
}
|
||||
@@ -481,8 +464,11 @@ class AppController {
|
||||
|
||||
Future<void> checkUpdateResultHandle({
|
||||
Map<String, dynamic>? data,
|
||||
bool isUser = false,
|
||||
bool handleError = false,
|
||||
}) async {
|
||||
if (globalState.isPre) {
|
||||
return;
|
||||
}
|
||||
if (data != null) {
|
||||
final tagName = data['tag_name'];
|
||||
final body = data['body'];
|
||||
@@ -500,16 +486,12 @@ class AppController {
|
||||
],
|
||||
),
|
||||
confirmText: appLocalizations.goDownload,
|
||||
cancelText: isUser ? null : appLocalizations.noLongerRemind,
|
||||
);
|
||||
if (res == true) {
|
||||
launchUrl(Uri.parse('https://github.com/$repository/releases/latest'));
|
||||
} else if (!isUser && res == false) {
|
||||
_ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.update((state) => state.copyWith(autoCheckUpdate: false));
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
} else if (isUser) {
|
||||
launchUrl(Uri.parse('https://github.com/$repository/releases/latest'));
|
||||
} else if (handleError) {
|
||||
globalState.showMessage(
|
||||
title: appLocalizations.checkUpdate,
|
||||
message: TextSpan(text: appLocalizations.checkUpdateError),
|
||||
@@ -547,7 +529,6 @@ class AppController {
|
||||
FlutterError.onError = (details) {
|
||||
commonPrint.log(
|
||||
'exception: ${details.exception} stack: ${details.stack}',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
};
|
||||
updateTray(true);
|
||||
@@ -570,11 +551,7 @@ class AppController {
|
||||
|
||||
Future<void> _connectCore() async {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connecting;
|
||||
final result = await Future.wait([
|
||||
coreController.preload(),
|
||||
if (!globalState.isService) Future.delayed(Duration(milliseconds: 300)),
|
||||
]);
|
||||
final String message = result[0];
|
||||
final message = await coreController.preload();
|
||||
if (message.isNotEmpty) {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
if (context.mounted) {
|
||||
@@ -801,17 +778,11 @@ class AppController {
|
||||
updateStatus(!_ref.read(isStartProvider));
|
||||
}
|
||||
|
||||
void updateSpeedStatistics() {
|
||||
_ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.update((state) => state.copyWith(showTrayTitle: !state.showTrayTitle));
|
||||
}
|
||||
|
||||
void updateCurrentSelectedMap(String groupName, String proxyName) {
|
||||
final currentProfile = _ref.read(currentProfileProvider);
|
||||
if (currentProfile != null &&
|
||||
currentProfile.selectedMap[groupName] != proxyName) {
|
||||
final selectedMap = Map<String, String>.from(currentProfile.selectedMap)
|
||||
final SelectedMap selectedMap = Map.from(currentProfile.selectedMap)
|
||||
..[groupName] = proxyName;
|
||||
_ref
|
||||
.read(profilesProvider.notifier)
|
||||
@@ -916,7 +887,7 @@ class AppController {
|
||||
json.decode(utf8.decode(configFile.content)),
|
||||
);
|
||||
for (final profile in profiles) {
|
||||
final filePath = join(homeDirPath, posix.normalize(profile.name));
|
||||
final filePath = join(homeDirPath, profile.name);
|
||||
final file = File(filePath);
|
||||
await file.create(recursive: true);
|
||||
await file.writeAsBytes(profile.content);
|
||||
@@ -963,27 +934,14 @@ class AppController {
|
||||
_ref.read(overrideDnsProvider.notifier).value = config.overrideDns;
|
||||
_ref.read(networkSettingProvider.notifier).value = config.networkProps;
|
||||
_ref.read(hotKeyActionsProvider.notifier).value = config.hotKeyActions;
|
||||
_ref.read(scriptsProvider.notifier).value = config.scripts;
|
||||
_ref.read(rulesProvider.notifier).value = config.rules;
|
||||
_ref.read(scriptStateProvider.notifier).value = config.scriptProps;
|
||||
}
|
||||
final currentProfile = _ref.read(currentProfileProvider);
|
||||
if (currentProfile == null && profiles.isNotEmpty) {
|
||||
if (currentProfile == null) {
|
||||
_ref.read(currentProfileIdProvider.notifier).value = profiles.first.id;
|
||||
}
|
||||
}
|
||||
|
||||
void checkNeedSetup() {
|
||||
if (!globalState.isStart) {
|
||||
return;
|
||||
}
|
||||
final profileId = _ref.read(currentProfileIdProvider);
|
||||
final setupState = globalState.getSetupState(profileId);
|
||||
if (!setupState.needSetup(globalState.lastSetupState)) {
|
||||
return;
|
||||
}
|
||||
setupClashConfigDebounce();
|
||||
}
|
||||
|
||||
Future<T?> safeRun<T>(
|
||||
FutureOr<T> Function() futureFunction, {
|
||||
String? title,
|
||||
@@ -993,12 +951,12 @@ class AppController {
|
||||
final realSilence = needLoading == true ? true : silence;
|
||||
try {
|
||||
if (needLoading) {
|
||||
_ref.read(loadingProvider.notifier).start();
|
||||
_ref.read(loadingProvider.notifier).value = true;
|
||||
}
|
||||
final res = await futureFunction();
|
||||
return res;
|
||||
} catch (e) {
|
||||
commonPrint.log('$title===> $e', logLevel: LogLevel.warning);
|
||||
commonPrint.log('$futureFunction ===> $e');
|
||||
if (realSilence) {
|
||||
globalState.showNotifier(e.toString());
|
||||
} else {
|
||||
@@ -1009,7 +967,7 @@ class AppController {
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
_ref.read(loadingProvider.notifier).stop();
|
||||
_ref.read(loadingProvider.notifier).value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ class CoreController {
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
bool get isCompleted => _interface.completer.isCompleted;
|
||||
|
||||
Future<String> preload() {
|
||||
return _interface.preload();
|
||||
}
|
||||
@@ -73,42 +71,24 @@ class CoreController {
|
||||
|
||||
FutureOr<bool> get isInit => _interface.isInit;
|
||||
|
||||
Future<String> validateConfig(String data) async {
|
||||
final path = await appPath.validateFilePath;
|
||||
await globalState.genValidateFile(path, data);
|
||||
final res = await _interface.validateConfig(path);
|
||||
await File(path).delete();
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<String> validateConfigFormBytes(Uint8List bytes) async {
|
||||
final path = await appPath.validateFilePath;
|
||||
await globalState.genValidateFileFormBytes(path, bytes);
|
||||
final res = await _interface.validateConfig(path);
|
||||
await File(path).delete();
|
||||
return res;
|
||||
FutureOr<String> validateConfig(String data) {
|
||||
return _interface.validateConfig(data);
|
||||
}
|
||||
|
||||
Future<String> updateConfig(UpdateParams updateParams) async {
|
||||
return await _interface.updateConfig(updateParams);
|
||||
}
|
||||
|
||||
Future<String> setupConfig({
|
||||
required SetupParams params,
|
||||
required SetupState setupState,
|
||||
VoidCallback? preloadInvoke,
|
||||
}) async {
|
||||
final res = _interface.setupConfig(params);
|
||||
if (preloadInvoke != null) {
|
||||
preloadInvoke();
|
||||
}
|
||||
return res;
|
||||
Future<String> setupConfig(ClashConfig clashConfig) async {
|
||||
await globalState.genConfigFile(clashConfig);
|
||||
final params = await globalState.getSetupParams();
|
||||
return await _interface.setupConfig(params);
|
||||
}
|
||||
|
||||
Future<List<Group>> getProxiesGroups({
|
||||
required ProxiesSortType sortType,
|
||||
required DelayMap delayMap,
|
||||
required Map<String, String> selectedMap,
|
||||
required SelectedMap selectedMap,
|
||||
required String defaultTestUrl,
|
||||
}) async {
|
||||
final proxies = await _interface.getProxies();
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'dart:isolate';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
mixin CoreInterface {
|
||||
Future<bool> init(InitParams params);
|
||||
@@ -18,7 +17,7 @@ mixin CoreInterface {
|
||||
|
||||
Future<bool> forceGc();
|
||||
|
||||
Future<String> validateConfig(String path);
|
||||
Future<String> validateConfig(String data);
|
||||
|
||||
Future<Result> getConfig(String path);
|
||||
|
||||
@@ -77,7 +76,7 @@ mixin CoreInterface {
|
||||
}
|
||||
|
||||
abstract class CoreHandlerInterface with CoreInterface {
|
||||
Completer get completer;
|
||||
Future get connected;
|
||||
|
||||
FutureOr<bool> destroy();
|
||||
|
||||
@@ -86,27 +85,8 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
dynamic data,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
try {
|
||||
await completer.future.timeout(const Duration(seconds: 10));
|
||||
} catch (e) {
|
||||
commonPrint.log(
|
||||
'Invoke pre ${method.name} timeout $e',
|
||||
logLevel: LogLevel.error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (kDebugMode) {
|
||||
commonPrint.log('Invoke ${method.name} ${DateTime.now()} $data');
|
||||
}
|
||||
|
||||
return utils.handleWatch(
|
||||
function: () async {
|
||||
return await invoke(method: method, data: data, timeout: timeout);
|
||||
},
|
||||
onWatch: (data, elapsedMilliseconds) {
|
||||
commonPrint.log('Invoke ${method.name} ${elapsedMilliseconds}ms');
|
||||
},
|
||||
);
|
||||
await connected;
|
||||
return invoke(method: method, data: data, timeout: timeout);
|
||||
}
|
||||
|
||||
Future<T?> invoke<T>({
|
||||
@@ -145,10 +125,10 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> validateConfig(String path) async {
|
||||
Future<String> validateConfig(String data) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.validateConfig,
|
||||
data: path,
|
||||
data: data,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
@@ -165,7 +145,7 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
@override
|
||||
Future<Result> getConfig(String path) async {
|
||||
return await _invoke<Result>(method: ActionMethod.getConfig, data: path) ??
|
||||
Result.success({});
|
||||
Result<Map<String, dynamic>>.success({});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -62,7 +62,7 @@ class CoreLib extends CoreHandlerInterface {
|
||||
}
|
||||
|
||||
@override
|
||||
Completer get completer => _connectedCompleter;
|
||||
Future get connected => _connectedCompleter.future;
|
||||
}
|
||||
|
||||
CoreLib? get coreLib => system.isAndroid ? CoreLib() : null;
|
||||
|
||||
@@ -38,29 +38,23 @@ class CoreService extends CoreHandlerInterface {
|
||||
completer?.complete(data);
|
||||
}
|
||||
|
||||
Future<void> _initServer() async {
|
||||
final server = await retry(
|
||||
task: () async {
|
||||
try {
|
||||
final address = !system.isWindows
|
||||
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
|
||||
: InternetAddress(localhost, type: InternetAddressType.IPv4);
|
||||
await _deleteSocketFile();
|
||||
final server = await ServerSocket.bind(address, 0, shared: true);
|
||||
server.listen((socket) async {
|
||||
await _attachSocket(socket);
|
||||
});
|
||||
return server;
|
||||
} catch (_) {
|
||||
return null;
|
||||
void _initServer() {
|
||||
runZonedGuarded(
|
||||
() async {
|
||||
final address = !system.isWindows
|
||||
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
|
||||
: InternetAddress(localhost, type: InternetAddressType.IPv4);
|
||||
await _deleteSocketFile();
|
||||
final server = await ServerSocket.bind(address, 0, shared: true);
|
||||
_serverCompleter.complete(server);
|
||||
await for (final socket in server) {
|
||||
await _attachSocket(socket);
|
||||
}
|
||||
},
|
||||
retryIf: (server) => server == null,
|
||||
(error, stack) async {
|
||||
commonPrint.log('Service error: $error');
|
||||
},
|
||||
);
|
||||
if (server == null) {
|
||||
exit(0);
|
||||
}
|
||||
_serverCompleter.complete(server);
|
||||
}
|
||||
|
||||
Future<void> _attachSocket(Socket socket) async {
|
||||
@@ -103,7 +97,7 @@ class CoreService extends CoreHandlerInterface {
|
||||
_process?.stderr.listen((e) {
|
||||
final error = utf8.decode(e);
|
||||
if (error.isNotEmpty) {
|
||||
commonPrint.log(error, logLevel: LogLevel.warning);
|
||||
commonPrint.log(error);
|
||||
}
|
||||
});
|
||||
await _socketCompleter.future;
|
||||
@@ -112,7 +106,6 @@ class CoreService extends CoreHandlerInterface {
|
||||
@override
|
||||
destroy() async {
|
||||
final server = await _serverCompleter.future;
|
||||
await shutdown();
|
||||
await server.close();
|
||||
await _deleteSocketFile();
|
||||
return true;
|
||||
@@ -187,7 +180,9 @@ class CoreService extends CoreHandlerInterface {
|
||||
}
|
||||
|
||||
@override
|
||||
Completer get completer => _socketCompleter;
|
||||
Future get connected {
|
||||
return _socketCompleter.future;
|
||||
}
|
||||
}
|
||||
|
||||
final coreService = system.isDesktop ? CoreService() : null;
|
||||
|
||||
@@ -197,7 +197,7 @@ extension KeyboardModifierExt on KeyboardModifier {
|
||||
|
||||
enum HotAction { start, view, mode, proxy, tun }
|
||||
|
||||
enum ProxiesIconStyle { none, standard, icon }
|
||||
enum ProxiesIconStyle { standard, none, icon }
|
||||
|
||||
enum FontFamily {
|
||||
twEmoji('Twemoji'),
|
||||
@@ -280,7 +280,6 @@ enum FunctionTag {
|
||||
logs,
|
||||
requests,
|
||||
autoScrollToEnd,
|
||||
loadedProvider,
|
||||
}
|
||||
|
||||
enum DashboardWidget {
|
||||
@@ -369,18 +368,6 @@ enum RuleAction {
|
||||
final String value;
|
||||
|
||||
const RuleAction(this.value);
|
||||
|
||||
static List<RuleAction> get addedRuleActions {
|
||||
return RuleAction.values
|
||||
.where(
|
||||
(item) => ![
|
||||
RuleAction.MATCH,
|
||||
RuleAction.RULE_SET,
|
||||
RuleAction.SUB_RULE,
|
||||
].contains(item),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
extension RuleActionExt on RuleAction {
|
||||
@@ -397,25 +384,18 @@ extension RuleActionExt on RuleAction {
|
||||
|
||||
enum OverrideRuleType { override, added }
|
||||
|
||||
enum OverwriteType {
|
||||
// none,
|
||||
standard,
|
||||
script,
|
||||
// custom,
|
||||
}
|
||||
|
||||
enum RuleTarget { DIRECT, REJECT, MATCH }
|
||||
enum RuleTarget { DIRECT, REJECT }
|
||||
|
||||
enum RecoveryStrategy { compatible, override }
|
||||
|
||||
enum CacheTag { logs, rules, requests, proxiesList }
|
||||
|
||||
enum Language { yaml, javaScript, json }
|
||||
enum Language { yaml, javaScript }
|
||||
|
||||
enum ImportOption { file, url }
|
||||
|
||||
enum ScrollPositionCacheKey { tools, profiles, proxiesList, proxiesTabList }
|
||||
|
||||
enum QueryTag { proxies, access }
|
||||
enum QueryTag { proxies }
|
||||
|
||||
enum CoreStatus { connecting, connected, disconnected }
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export 'overwrite/overwrite.dart';
|
||||
@@ -1 +0,0 @@
|
||||
export 'rule.dart';
|
||||
@@ -1,303 +0,0 @@
|
||||
library;
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/clash_config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/card.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:fl_clash/widgets/input.dart';
|
||||
import 'package:fl_clash/widgets/list.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RuleItem extends StatelessWidget {
|
||||
final bool isSelected;
|
||||
final bool isEditing;
|
||||
final Rule rule;
|
||||
final void Function(String id) onSelected;
|
||||
final void Function(Rule rule) onEdit;
|
||||
|
||||
const RuleItem({
|
||||
super.key,
|
||||
required this.isSelected,
|
||||
required this.rule,
|
||||
required this.onSelected,
|
||||
required this.onEdit,
|
||||
this.isEditing = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonSelectedListItem(
|
||||
isSelected: isSelected,
|
||||
onSelected: () {
|
||||
onSelected(rule.id);
|
||||
},
|
||||
title: Text(
|
||||
rule.value,
|
||||
style: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
),
|
||||
onPressed: () {
|
||||
onEdit(rule);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RuleStatusItem extends StatelessWidget {
|
||||
final bool status;
|
||||
final Rule rule;
|
||||
final void Function(bool) onChange;
|
||||
|
||||
const RuleStatusItem({
|
||||
super.key,
|
||||
required this.status,
|
||||
required this.rule,
|
||||
required this.onChange,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 4),
|
||||
child: CommonCard(
|
||||
padding: EdgeInsets.zero,
|
||||
radius: 18,
|
||||
type: CommonCardType.filled,
|
||||
onPressed: () {
|
||||
onChange(!status);
|
||||
},
|
||||
child: ListTile(
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 0,
|
||||
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
trailing: Switch(value: status, onChanged: onChange),
|
||||
title: Text(rule.value),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddOrEditRuleDialog extends StatefulWidget {
|
||||
final Rule? rule;
|
||||
|
||||
const AddOrEditRuleDialog({super.key, this.rule});
|
||||
|
||||
@override
|
||||
State<AddOrEditRuleDialog> createState() => _AddOrEditRuleDialogState();
|
||||
}
|
||||
|
||||
class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
|
||||
late RuleAction _ruleAction;
|
||||
final _ruleTargetController = TextEditingController();
|
||||
final _contentController = TextEditingController();
|
||||
bool _noResolve = false;
|
||||
bool _src = false;
|
||||
List<DropdownMenuEntry> _targetItems = [];
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_initState();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _initState() {
|
||||
_targetItems = [
|
||||
...RuleTarget.values.map(
|
||||
(item) => DropdownMenuEntry(value: item.name, label: item.name),
|
||||
),
|
||||
];
|
||||
if (widget.rule != null) {
|
||||
final parsedRule = ParsedRule.parseString(widget.rule!.value);
|
||||
_ruleAction = parsedRule.ruleAction;
|
||||
_contentController.text = parsedRule.content ?? '';
|
||||
_ruleTargetController.text = parsedRule.ruleTarget ?? '';
|
||||
_noResolve = parsedRule.noResolve;
|
||||
_src = parsedRule.src;
|
||||
return;
|
||||
}
|
||||
_ruleAction = RuleAction.addedRuleActions.first;
|
||||
if (_targetItems.isNotEmpty) {
|
||||
_ruleTargetController.text = _targetItems.first.value;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(AddOrEditRuleDialog oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.rule != widget.rule) {
|
||||
_initState();
|
||||
}
|
||||
}
|
||||
|
||||
void _handleSubmit() {
|
||||
final res = _formKey.currentState?.validate();
|
||||
if (res == false) {
|
||||
return;
|
||||
}
|
||||
final parsedRule = ParsedRule(
|
||||
ruleAction: _ruleAction,
|
||||
content: _contentController.text,
|
||||
ruleTarget: _ruleTargetController.text,
|
||||
noResolve: _noResolve,
|
||||
src: _src,
|
||||
);
|
||||
final rule = widget.rule != null
|
||||
? widget.rule!.copyWith(value: parsedRule.value)
|
||||
: Rule.value(parsedRule.value);
|
||||
Navigator.of(context).pop(rule);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonDialog(
|
||||
title: widget.rule != null
|
||||
? appLocalizations.editRule
|
||||
: appLocalizations.addRule,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _handleSubmit,
|
||||
child: Text(appLocalizations.confirm),
|
||||
),
|
||||
],
|
||||
child: DropdownMenuTheme(
|
||||
data: DropdownMenuThemeData(
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
border: OutlineInputBorder(),
|
||||
labelStyle: context.textTheme.bodyLarge?.copyWith(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
FilledButton.tonal(
|
||||
onPressed: () async {
|
||||
_ruleAction =
|
||||
await globalState.showCommonDialog<RuleAction>(
|
||||
filter: false,
|
||||
child: OptionsDialog<RuleAction>(
|
||||
title: appLocalizations.ruleName,
|
||||
options: RuleAction.addedRuleActions,
|
||||
textBuilder: (item) => item.value,
|
||||
value: _ruleAction,
|
||||
),
|
||||
) ??
|
||||
_ruleAction;
|
||||
setState(() {});
|
||||
},
|
||||
child: Text(_ruleAction.value),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.text,
|
||||
onFieldSubmitted: (_) {
|
||||
_handleSubmit();
|
||||
},
|
||||
controller: _contentController,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: appLocalizations.content,
|
||||
),
|
||||
validator: (_) {
|
||||
if (_contentController.text.isEmpty) {
|
||||
return appLocalizations.emptyTip(
|
||||
appLocalizations.content,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
FormField<String>(
|
||||
validator: (_) {
|
||||
if (_ruleTargetController.text.isEmpty) {
|
||||
return appLocalizations.emptyTip(
|
||||
appLocalizations.ruleTarget,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
builder: (filed) {
|
||||
return DropdownMenu(
|
||||
controller: _ruleTargetController,
|
||||
label: Text(appLocalizations.ruleTarget),
|
||||
width: 200,
|
||||
menuHeight: 250,
|
||||
enableFilter: false,
|
||||
enableSearch: false,
|
||||
dropdownMenuEntries: _targetItems,
|
||||
errorText: filed.errorText,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (_ruleAction.hasParams) ...[
|
||||
SizedBox(height: 20),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
children: [
|
||||
CommonCard(
|
||||
radius: 8,
|
||||
isSelected: _src,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Text(
|
||||
appLocalizations.sourceIp,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_src = !_src;
|
||||
});
|
||||
},
|
||||
),
|
||||
CommonCard(
|
||||
radius: 8,
|
||||
isSelected: _noResolve,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Text(
|
||||
appLocalizations.noResolve,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_noResolve = !_noResolve;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -20,42 +20,27 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'en';
|
||||
|
||||
static String m0(count) =>
|
||||
"${Intl.plural(count, one: '1 day ago', other: '${count} days ago')}";
|
||||
|
||||
static String m1(label) =>
|
||||
static String m0(label) =>
|
||||
"Are you sure you want to delete the selected ${label}?";
|
||||
|
||||
static String m2(label) =>
|
||||
static String m1(label) =>
|
||||
"Are you sure you want to delete the current ${label}?";
|
||||
|
||||
static String m3(label) => "${label} details";
|
||||
static String m2(label) => "${label} details";
|
||||
|
||||
static String m4(label) => "${label} cannot be empty";
|
||||
static String m3(label) => "${label} cannot be empty";
|
||||
|
||||
static String m5(label) => "Current ${label} already exists";
|
||||
static String m4(label) => "Current ${label} already exists";
|
||||
|
||||
static String m6(count) =>
|
||||
"${Intl.plural(count, one: '1 hour ago', other: '${count} hours ago')}";
|
||||
static String m5(label) => "No ${label} at the moment";
|
||||
|
||||
static String m7(count) =>
|
||||
"${Intl.plural(count, one: '1 minute ago', other: '${count} minutes ago')}";
|
||||
static String m6(label) => "${label} must be a number";
|
||||
|
||||
static String m8(count) =>
|
||||
"${Intl.plural(count, one: '1 month ago', other: '${count} months ago')}";
|
||||
static String m7(label) => "${label} must be between 1024 and 49151";
|
||||
|
||||
static String m9(label) => "No ${label} yet";
|
||||
static String m8(count) => "${count} items have been selected";
|
||||
|
||||
static String m10(label) => "${label} must be a number";
|
||||
|
||||
static String m11(label) => "${label} must be between 1024 and 49151";
|
||||
|
||||
static String m12(count) => "${count} items have been selected";
|
||||
|
||||
static String m13(label) => "${label} must be a url";
|
||||
|
||||
static String m14(count) =>
|
||||
"${Intl.plural(count, one: '1 year ago', other: '${count} years ago')}";
|
||||
static String m9(label) => "${label} must be a url";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@@ -70,9 +55,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"accessControlNotAllowDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"The selected application will be excluded from VPN",
|
||||
),
|
||||
"accessControlSettings": MessageLookupByLibrary.simpleMessage(
|
||||
"Access Control Settings",
|
||||
),
|
||||
"account": MessageLookupByLibrary.simpleMessage("Account"),
|
||||
"action": MessageLookupByLibrary.simpleMessage("Action"),
|
||||
"action_mode": MessageLookupByLibrary.simpleMessage("Switch mode"),
|
||||
@@ -85,7 +67,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"addedOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Attach on the original rules",
|
||||
),
|
||||
"addedRules": MessageLookupByLibrary.simpleMessage("Added rules"),
|
||||
"address": MessageLookupByLibrary.simpleMessage("Address"),
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage(
|
||||
"WebDAV server address",
|
||||
@@ -99,12 +80,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Boot up by using admin mode",
|
||||
),
|
||||
"advancedConfig": MessageLookupByLibrary.simpleMessage(
|
||||
"Advanced configuration",
|
||||
),
|
||||
"advancedConfigDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Provide diverse configuration options",
|
||||
),
|
||||
"ago": MessageLookupByLibrary.simpleMessage(" Ago"),
|
||||
"agree": MessageLookupByLibrary.simpleMessage("Agree"),
|
||||
"allApps": MessageLookupByLibrary.simpleMessage("All apps"),
|
||||
@@ -125,12 +100,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"appDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Processing app related settings",
|
||||
),
|
||||
"appendSystemDns": MessageLookupByLibrary.simpleMessage(
|
||||
"Append System DNS",
|
||||
),
|
||||
"appendSystemDnsTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Forcefully append system DNS to the configuration",
|
||||
),
|
||||
"application": MessageLookupByLibrary.simpleMessage("Application"),
|
||||
"applicationDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Modify application related settings",
|
||||
@@ -208,12 +177,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Opening it will lose part of its application ability and gain the support of full amount of Clash.",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("Confirm"),
|
||||
"confirmClearAllData": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure you want to clear all data?",
|
||||
),
|
||||
"confirmForceCrashCore": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure you want to force crash the core?",
|
||||
),
|
||||
"connected": MessageLookupByLibrary.simpleMessage("Connected"),
|
||||
"connecting": MessageLookupByLibrary.simpleMessage("Connecting..."),
|
||||
"connection": MessageLookupByLibrary.simpleMessage("Connection"),
|
||||
@@ -225,9 +188,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"contactMe": MessageLookupByLibrary.simpleMessage("Contact me"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("Content"),
|
||||
"contentScheme": MessageLookupByLibrary.simpleMessage("Content"),
|
||||
"controlGlobalAddedRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Control global added rules",
|
||||
),
|
||||
"copy": MessageLookupByLibrary.simpleMessage("Copy"),
|
||||
"copyEnvVar": MessageLookupByLibrary.simpleMessage(
|
||||
"Copying environment variables",
|
||||
@@ -235,9 +195,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"copyLink": MessageLookupByLibrary.simpleMessage("Copy link"),
|
||||
"copySuccess": MessageLookupByLibrary.simpleMessage("Copy success"),
|
||||
"core": MessageLookupByLibrary.simpleMessage("Core"),
|
||||
"coreConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
|
||||
"Core configuration change detected",
|
||||
),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
|
||||
"coreStatus": MessageLookupByLibrary.simpleMessage("Core status"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("Country"),
|
||||
@@ -258,7 +215,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Data Collection Notice",
|
||||
),
|
||||
"days": MessageLookupByLibrary.simpleMessage("Days"),
|
||||
"daysAgo": m0,
|
||||
"defaultNameserver": MessageLookupByLibrary.simpleMessage(
|
||||
"Default nameserver",
|
||||
),
|
||||
@@ -270,8 +226,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"delay": MessageLookupByLibrary.simpleMessage("Delay"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
"deleteMultipTip": m0,
|
||||
"deleteTip": m1,
|
||||
"desc": MessageLookupByLibrary.simpleMessage(
|
||||
"A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.",
|
||||
),
|
||||
@@ -282,7 +238,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"destinationIPASN": MessageLookupByLibrary.simpleMessage(
|
||||
"Destination IPASN",
|
||||
),
|
||||
"details": m3,
|
||||
"details": m2,
|
||||
"detectionTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Relying on third-party api is for reference only",
|
||||
),
|
||||
@@ -313,11 +269,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"domain": MessageLookupByLibrary.simpleMessage("Domain"),
|
||||
"download": MessageLookupByLibrary.simpleMessage("Download"),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
|
||||
"editGlobalRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Edit global rules",
|
||||
),
|
||||
"editRule": MessageLookupByLibrary.simpleMessage("Edit rule"),
|
||||
"emptyTip": m4,
|
||||
"emptyTip": m3,
|
||||
"en": MessageLookupByLibrary.simpleMessage("English"),
|
||||
"enableOverride": MessageLookupByLibrary.simpleMessage("Enable override"),
|
||||
"entries": MessageLookupByLibrary.simpleMessage(" entries"),
|
||||
@@ -325,7 +277,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"excludeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"When the app is in the background, the app is hidden from the recent task",
|
||||
),
|
||||
"existsTip": m5,
|
||||
"existsTip": m4,
|
||||
"exit": MessageLookupByLibrary.simpleMessage("Exit"),
|
||||
"expand": MessageLookupByLibrary.simpleMessage("Standard"),
|
||||
"expirationTime": MessageLookupByLibrary.simpleMessage("Expiration time"),
|
||||
@@ -339,7 +291,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"externalControllerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Once enabled, the Clash kernel can be controlled on port 9090",
|
||||
),
|
||||
"externalFetch": MessageLookupByLibrary.simpleMessage("External fetch"),
|
||||
"externalLink": MessageLookupByLibrary.simpleMessage("External link"),
|
||||
"externalResources": MessageLookupByLibrary.simpleMessage(
|
||||
"External resources",
|
||||
@@ -388,9 +339,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"global": MessageLookupByLibrary.simpleMessage("Global"),
|
||||
"go": MessageLookupByLibrary.simpleMessage("Go"),
|
||||
"goDownload": MessageLookupByLibrary.simpleMessage("Go to download"),
|
||||
"goToConfigureScript": MessageLookupByLibrary.simpleMessage(
|
||||
"Go to configure script",
|
||||
),
|
||||
"hasCacheChange": MessageLookupByLibrary.simpleMessage(
|
||||
"Do you want to cache the changes?",
|
||||
),
|
||||
@@ -404,7 +352,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Use keyboard to control applications",
|
||||
),
|
||||
"hours": MessageLookupByLibrary.simpleMessage("Hours"),
|
||||
"hoursAgo": m6,
|
||||
"icon": MessageLookupByLibrary.simpleMessage("Icon"),
|
||||
"iconConfiguration": MessageLookupByLibrary.simpleMessage(
|
||||
"Icon configuration",
|
||||
@@ -434,7 +381,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"ja": MessageLookupByLibrary.simpleMessage("Japanese"),
|
||||
"just": MessageLookupByLibrary.simpleMessage("Just"),
|
||||
"justNow": MessageLookupByLibrary.simpleMessage("Just now"),
|
||||
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Tcp keep alive interval",
|
||||
),
|
||||
@@ -444,8 +390,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"light": MessageLookupByLibrary.simpleMessage("Light"),
|
||||
"list": MessageLookupByLibrary.simpleMessage("List"),
|
||||
"listen": MessageLookupByLibrary.simpleMessage("Listen"),
|
||||
"loadTest": MessageLookupByLibrary.simpleMessage("Load test"),
|
||||
"loading": MessageLookupByLibrary.simpleMessage("Loading..."),
|
||||
"local": MessageLookupByLibrary.simpleMessage("Local"),
|
||||
"localBackupDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Backup local data to local",
|
||||
@@ -478,12 +422,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Modify the default system exit event",
|
||||
),
|
||||
"minutes": MessageLookupByLibrary.simpleMessage("Minutes"),
|
||||
"minutesAgo": m7,
|
||||
"mixedPort": MessageLookupByLibrary.simpleMessage("Mixed Port"),
|
||||
"mode": MessageLookupByLibrary.simpleMessage("Mode"),
|
||||
"monochromeScheme": MessageLookupByLibrary.simpleMessage("Monochrome"),
|
||||
"months": MessageLookupByLibrary.simpleMessage("Months"),
|
||||
"monthsAgo": m8,
|
||||
"more": MessageLookupByLibrary.simpleMessage("More"),
|
||||
"name": MessageLookupByLibrary.simpleMessage("Name"),
|
||||
"nameSort": MessageLookupByLibrary.simpleMessage("Sort by name"),
|
||||
@@ -511,9 +453,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noHotKey": MessageLookupByLibrary.simpleMessage("No HotKey"),
|
||||
"noIcon": MessageLookupByLibrary.simpleMessage("None"),
|
||||
"noInfo": MessageLookupByLibrary.simpleMessage("No info"),
|
||||
"noLongerRemind": MessageLookupByLibrary.simpleMessage(
|
||||
"Don\'t remind again",
|
||||
),
|
||||
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("No more info"),
|
||||
"noNetwork": MessageLookupByLibrary.simpleMessage("No network"),
|
||||
"noNetworkApp": MessageLookupByLibrary.simpleMessage("No network APP"),
|
||||
@@ -529,8 +468,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"No profile, Please add a profile",
|
||||
),
|
||||
"nullTip": m9,
|
||||
"numberTip": m10,
|
||||
"nullTip": m5,
|
||||
"numberTip": m6,
|
||||
"oneColumn": MessageLookupByLibrary.simpleMessage("One column"),
|
||||
"onlyIcon": MessageLookupByLibrary.simpleMessage("Icon"),
|
||||
"onlyOtherApps": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -559,11 +498,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Does not take effect in script mode",
|
||||
),
|
||||
"overrideMode": MessageLookupByLibrary.simpleMessage("Override mode"),
|
||||
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Override the original rule",
|
||||
),
|
||||
"overrideScript": MessageLookupByLibrary.simpleMessage("Override script"),
|
||||
"palette": MessageLookupByLibrary.simpleMessage("Palette"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("Password"),
|
||||
"paste": MessageLookupByLibrary.simpleMessage("Paste"),
|
||||
@@ -586,7 +523,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"portConflictTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Please enter a different port",
|
||||
),
|
||||
"portTip": m11,
|
||||
"portTip": m7,
|
||||
"preferH3Desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Prioritize the use of DOH\'s http/3",
|
||||
),
|
||||
@@ -660,7 +597,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"redirPort": MessageLookupByLibrary.simpleMessage("Redir Port"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("redo"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("RegExp"),
|
||||
"reload": MessageLookupByLibrary.simpleMessage("Reload"),
|
||||
"remote": MessageLookupByLibrary.simpleMessage("Remote"),
|
||||
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Backup local data to WebDAV",
|
||||
@@ -679,9 +615,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"View recently request records",
|
||||
),
|
||||
"reset": MessageLookupByLibrary.simpleMessage("Reset"),
|
||||
"resetPageChangesTip": MessageLookupByLibrary.simpleMessage(
|
||||
"The current page has changes. Are you sure you want to reset?",
|
||||
),
|
||||
"resetTip": MessageLookupByLibrary.simpleMessage("Make sure to reset"),
|
||||
"resources": MessageLookupByLibrary.simpleMessage("Resources"),
|
||||
"resourcesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -691,7 +624,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS connection following rules, need to configure proxy-server-nameserver",
|
||||
),
|
||||
"restart": MessageLookupByLibrary.simpleMessage("Restart"),
|
||||
"restartCoreTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure you want to restart the core?",
|
||||
),
|
||||
@@ -717,14 +649,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Are you sure you want to save?",
|
||||
),
|
||||
"script": MessageLookupByLibrary.simpleMessage("Script"),
|
||||
"scriptModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Script mode, use external extension scripts, provide one-click override configuration capability",
|
||||
),
|
||||
"search": MessageLookupByLibrary.simpleMessage("Search"),
|
||||
"seconds": MessageLookupByLibrary.simpleMessage("Seconds"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
|
||||
"selected": MessageLookupByLibrary.simpleMessage("Selected"),
|
||||
"selectedCountTitle": m12,
|
||||
"selectedCountTitle": m8,
|
||||
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
|
||||
"show": MessageLookupByLibrary.simpleMessage("Show"),
|
||||
"shrink": MessageLookupByLibrary.simpleMessage("Shrink"),
|
||||
@@ -739,12 +668,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sourceIp": MessageLookupByLibrary.simpleMessage("Source IP"),
|
||||
"specialProxy": MessageLookupByLibrary.simpleMessage("Special proxy"),
|
||||
"specialRules": MessageLookupByLibrary.simpleMessage("special rules"),
|
||||
"speedStatistics": MessageLookupByLibrary.simpleMessage("Speed statistics"),
|
||||
"stackMode": MessageLookupByLibrary.simpleMessage("Stack mode"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("Standard"),
|
||||
"standardModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Standard mode, override basic configuration, provide simple rule addition capability",
|
||||
),
|
||||
"start": MessageLookupByLibrary.simpleMessage("Start"),
|
||||
"startVpn": MessageLookupByLibrary.simpleMessage("Starting VPN..."),
|
||||
"status": MessageLookupByLibrary.simpleMessage("Status"),
|
||||
@@ -794,8 +719,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tunDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"only effective in administrator mode",
|
||||
),
|
||||
"turnOff": MessageLookupByLibrary.simpleMessage("Turn Off"),
|
||||
"turnOn": MessageLookupByLibrary.simpleMessage("Turn On"),
|
||||
"twoColumns": MessageLookupByLibrary.simpleMessage("Two columns"),
|
||||
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"unable to update current profile",
|
||||
@@ -813,15 +736,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"urlDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Obtain profile through URL",
|
||||
),
|
||||
"urlTip": m13,
|
||||
"urlTip": m9,
|
||||
"useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"),
|
||||
"useSystemHosts": MessageLookupByLibrary.simpleMessage("Use system hosts"),
|
||||
"value": MessageLookupByLibrary.simpleMessage("Value"),
|
||||
"vibrantScheme": MessageLookupByLibrary.simpleMessage("Vibrant"),
|
||||
"view": MessageLookupByLibrary.simpleMessage("View"),
|
||||
"vpnConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
|
||||
"VPN configuration change detected",
|
||||
),
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Modify VPN related settings",
|
||||
),
|
||||
@@ -839,7 +759,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"whitelistMode": MessageLookupByLibrary.simpleMessage("Whitelist mode"),
|
||||
"years": MessageLookupByLibrary.simpleMessage("Years"),
|
||||
"yearsAgo": m14,
|
||||
"zh_CN": MessageLookupByLibrary.simpleMessage("Simplified Chinese"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,35 +20,25 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'ja';
|
||||
|
||||
static String m0(count) => "${count}日前";
|
||||
static String m0(label) => "選択された${label}を削除してもよろしいですか?";
|
||||
|
||||
static String m1(label) => "選択された${label}を削除してもよろしいですか?";
|
||||
static String m1(label) => "現在の${label}を削除してもよろしいですか?";
|
||||
|
||||
static String m2(label) => "現在の${label}を削除してもよろしいですか?";
|
||||
static String m2(label) => "${label}詳細";
|
||||
|
||||
static String m3(label) => "${label}詳細";
|
||||
static String m3(label) => "${label}は空欄にできません";
|
||||
|
||||
static String m4(label) => "${label}は空欄にできません";
|
||||
static String m4(label) => "現在の${label}は既に存在しています";
|
||||
|
||||
static String m5(label) => "現在の${label}は既に存在しています";
|
||||
static String m5(label) => "現在${label}はありません";
|
||||
|
||||
static String m6(count) => "${count}時間前";
|
||||
static String m6(label) => "${label}は数字でなければなりません";
|
||||
|
||||
static String m7(count) => "${count}分前";
|
||||
static String m7(label) => "${label} は 1024 から 49151 の間でなければなりません";
|
||||
|
||||
static String m8(count) => "${count}ヶ月前";
|
||||
static String m8(count) => "${count} 項目が選択されています";
|
||||
|
||||
static String m9(label) => "まだ${label}はありません";
|
||||
|
||||
static String m10(label) => "${label}は数字でなければなりません";
|
||||
|
||||
static String m11(label) => "${label} は 1024 から 49151 の間でなければなりません";
|
||||
|
||||
static String m12(count) => "${count} 項目が選択されています";
|
||||
|
||||
static String m13(label) => "${label}はURLである必要があります";
|
||||
|
||||
static String m14(count) => "${count}年前";
|
||||
static String m9(label) => "${label}はURLである必要があります";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@@ -63,7 +53,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"accessControlNotAllowDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"選択したアプリをVPNから除外",
|
||||
),
|
||||
"accessControlSettings": MessageLookupByLibrary.simpleMessage("アクセス制御設定"),
|
||||
"account": MessageLookupByLibrary.simpleMessage("アカウント"),
|
||||
"action": MessageLookupByLibrary.simpleMessage("アクション"),
|
||||
"action_mode": MessageLookupByLibrary.simpleMessage("モード切替"),
|
||||
@@ -74,14 +63,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"add": MessageLookupByLibrary.simpleMessage("追加"),
|
||||
"addRule": MessageLookupByLibrary.simpleMessage("ルールを追加"),
|
||||
"addedOriginRules": MessageLookupByLibrary.simpleMessage("元のルールに追加"),
|
||||
"addedRules": MessageLookupByLibrary.simpleMessage("追加ルール"),
|
||||
"address": MessageLookupByLibrary.simpleMessage("アドレス"),
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAVサーバーアドレス"),
|
||||
"addressTip": MessageLookupByLibrary.simpleMessage("有効なWebDAVアドレスを入力"),
|
||||
"adminAutoLaunch": MessageLookupByLibrary.simpleMessage("管理者自動起動"),
|
||||
"adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage("管理者モードで起動"),
|
||||
"advancedConfig": MessageLookupByLibrary.simpleMessage("高度な設定"),
|
||||
"advancedConfigDesc": MessageLookupByLibrary.simpleMessage("多様な設定を提供"),
|
||||
"ago": MessageLookupByLibrary.simpleMessage("前"),
|
||||
"agree": MessageLookupByLibrary.simpleMessage("同意"),
|
||||
"allApps": MessageLookupByLibrary.simpleMessage("全アプリ"),
|
||||
@@ -94,10 +80,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"app": MessageLookupByLibrary.simpleMessage("アプリ"),
|
||||
"appAccessControl": MessageLookupByLibrary.simpleMessage("アプリアクセス制御"),
|
||||
"appDesc": MessageLookupByLibrary.simpleMessage("アプリ関連設定の処理"),
|
||||
"appendSystemDns": MessageLookupByLibrary.simpleMessage("システムDNSを追加"),
|
||||
"appendSystemDnsTip": MessageLookupByLibrary.simpleMessage(
|
||||
"設定にシステムDNSを強制的に追加します",
|
||||
),
|
||||
"application": MessageLookupByLibrary.simpleMessage("アプリケーション"),
|
||||
"applicationDesc": MessageLookupByLibrary.simpleMessage("アプリ関連設定を変更"),
|
||||
"auto": MessageLookupByLibrary.simpleMessage("自動"),
|
||||
@@ -151,12 +133,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"有効化すると一部機能を失いますが、Clashの完全サポートを獲得",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("確認"),
|
||||
"confirmClearAllData": MessageLookupByLibrary.simpleMessage(
|
||||
"すべてのデータをクリアしてもよろしいですか?",
|
||||
),
|
||||
"confirmForceCrashCore": MessageLookupByLibrary.simpleMessage(
|
||||
"コアを強制的にクラッシュさせてもよろしいですか?",
|
||||
),
|
||||
"connected": MessageLookupByLibrary.simpleMessage("接続済み"),
|
||||
"connecting": MessageLookupByLibrary.simpleMessage("接続中..."),
|
||||
"connection": MessageLookupByLibrary.simpleMessage("接続"),
|
||||
@@ -166,17 +142,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"contactMe": MessageLookupByLibrary.simpleMessage("連絡する"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("内容"),
|
||||
"contentScheme": MessageLookupByLibrary.simpleMessage("コンテンツテーマ"),
|
||||
"controlGlobalAddedRules": MessageLookupByLibrary.simpleMessage(
|
||||
"グローバル追加ルールを制御",
|
||||
),
|
||||
"copy": MessageLookupByLibrary.simpleMessage("コピー"),
|
||||
"copyEnvVar": MessageLookupByLibrary.simpleMessage("環境変数をコピー"),
|
||||
"copyLink": MessageLookupByLibrary.simpleMessage("リンクをコピー"),
|
||||
"copySuccess": MessageLookupByLibrary.simpleMessage("コピー成功"),
|
||||
"core": MessageLookupByLibrary.simpleMessage("コア"),
|
||||
"coreConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
|
||||
"コア設定の変更が検出されました",
|
||||
),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("コア情報"),
|
||||
"coreStatus": MessageLookupByLibrary.simpleMessage("コアステータス"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("国"),
|
||||
@@ -195,7 +165,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"dataCollectionTip": MessageLookupByLibrary.simpleMessage("データ収集説明"),
|
||||
"days": MessageLookupByLibrary.simpleMessage("日"),
|
||||
"daysAgo": m0,
|
||||
"defaultNameserver": MessageLookupByLibrary.simpleMessage("デフォルトネームサーバー"),
|
||||
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNSサーバーの解決用",
|
||||
@@ -205,15 +174,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"delay": MessageLookupByLibrary.simpleMessage("遅延"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("遅延順"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("削除"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
"deleteMultipTip": m0,
|
||||
"deleteTip": m1,
|
||||
"desc": MessageLookupByLibrary.simpleMessage(
|
||||
"ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
|
||||
),
|
||||
"destination": MessageLookupByLibrary.simpleMessage("宛先"),
|
||||
"destinationGeoIP": MessageLookupByLibrary.simpleMessage("宛先地理情報"),
|
||||
"destinationIPASN": MessageLookupByLibrary.simpleMessage("宛先IP ASN"),
|
||||
"details": m3,
|
||||
"details": m2,
|
||||
"detectionTip": MessageLookupByLibrary.simpleMessage("サードパーティAPIに依存(参考値)"),
|
||||
"developerMode": MessageLookupByLibrary.simpleMessage("デベロッパーモード"),
|
||||
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -234,9 +203,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"domain": MessageLookupByLibrary.simpleMessage("ドメイン"),
|
||||
"download": MessageLookupByLibrary.simpleMessage("ダウンロード"),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("編集"),
|
||||
"editGlobalRules": MessageLookupByLibrary.simpleMessage("グローバルルールを編集"),
|
||||
"editRule": MessageLookupByLibrary.simpleMessage("ルールを編集"),
|
||||
"emptyTip": m4,
|
||||
"emptyTip": m3,
|
||||
"en": MessageLookupByLibrary.simpleMessage("英語"),
|
||||
"enableOverride": MessageLookupByLibrary.simpleMessage("上書きを有効化"),
|
||||
"entries": MessageLookupByLibrary.simpleMessage(" エントリ"),
|
||||
@@ -244,7 +211,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"excludeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"アプリがバックグラウンド時に最近のタスクから非表示",
|
||||
),
|
||||
"existsTip": m5,
|
||||
"existsTip": m4,
|
||||
"exit": MessageLookupByLibrary.simpleMessage("終了"),
|
||||
"expand": MessageLookupByLibrary.simpleMessage("標準"),
|
||||
"expirationTime": MessageLookupByLibrary.simpleMessage("有効期限"),
|
||||
@@ -256,7 +223,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"externalControllerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"有効化するとClashコアをポート9090で制御可能",
|
||||
),
|
||||
"externalFetch": MessageLookupByLibrary.simpleMessage("外部取得"),
|
||||
"externalLink": MessageLookupByLibrary.simpleMessage("外部リンク"),
|
||||
"externalResources": MessageLookupByLibrary.simpleMessage("外部リソース"),
|
||||
"fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeipフィルター"),
|
||||
@@ -293,7 +259,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"global": MessageLookupByLibrary.simpleMessage("グローバル"),
|
||||
"go": MessageLookupByLibrary.simpleMessage("移動"),
|
||||
"goDownload": MessageLookupByLibrary.simpleMessage("ダウンロードへ"),
|
||||
"goToConfigureScript": MessageLookupByLibrary.simpleMessage("スクリプト設定に移動"),
|
||||
"hasCacheChange": MessageLookupByLibrary.simpleMessage("変更をキャッシュしますか?"),
|
||||
"host": MessageLookupByLibrary.simpleMessage("ホスト"),
|
||||
"hostsDesc": MessageLookupByLibrary.simpleMessage("ホストを追加"),
|
||||
@@ -303,7 +268,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"キーボードでアプリを制御",
|
||||
),
|
||||
"hours": MessageLookupByLibrary.simpleMessage("時間"),
|
||||
"hoursAgo": m6,
|
||||
"icon": MessageLookupByLibrary.simpleMessage("アイコン"),
|
||||
"iconConfiguration": MessageLookupByLibrary.simpleMessage("アイコン設定"),
|
||||
"iconStyle": MessageLookupByLibrary.simpleMessage("アイコンスタイル"),
|
||||
@@ -323,7 +287,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"ipv6InboundDesc": MessageLookupByLibrary.simpleMessage("IPv6インバウンドを許可"),
|
||||
"ja": MessageLookupByLibrary.simpleMessage("日本語"),
|
||||
"just": MessageLookupByLibrary.simpleMessage("たった今"),
|
||||
"justNow": MessageLookupByLibrary.simpleMessage("たった今"),
|
||||
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"TCPキープアライブ間隔",
|
||||
),
|
||||
@@ -333,8 +296,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"light": MessageLookupByLibrary.simpleMessage("ライト"),
|
||||
"list": MessageLookupByLibrary.simpleMessage("リスト"),
|
||||
"listen": MessageLookupByLibrary.simpleMessage("リスン"),
|
||||
"loadTest": MessageLookupByLibrary.simpleMessage("読み込みテスト"),
|
||||
"loading": MessageLookupByLibrary.simpleMessage("読み込み中..."),
|
||||
"local": MessageLookupByLibrary.simpleMessage("ローカル"),
|
||||
"localBackupDesc": MessageLookupByLibrary.simpleMessage("ローカルにデータをバックアップ"),
|
||||
"localRecoveryDesc": MessageLookupByLibrary.simpleMessage("ファイルからデータを復元"),
|
||||
@@ -357,12 +318,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"システムの終了イベントを変更",
|
||||
),
|
||||
"minutes": MessageLookupByLibrary.simpleMessage("分"),
|
||||
"minutesAgo": m7,
|
||||
"mixedPort": MessageLookupByLibrary.simpleMessage("混合ポート"),
|
||||
"mode": MessageLookupByLibrary.simpleMessage("モード"),
|
||||
"monochromeScheme": MessageLookupByLibrary.simpleMessage("モノクローム"),
|
||||
"months": MessageLookupByLibrary.simpleMessage("月"),
|
||||
"monthsAgo": m8,
|
||||
"more": MessageLookupByLibrary.simpleMessage("詳細"),
|
||||
"name": MessageLookupByLibrary.simpleMessage("名前"),
|
||||
"nameSort": MessageLookupByLibrary.simpleMessage("名前順"),
|
||||
@@ -382,7 +341,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noHotKey": MessageLookupByLibrary.simpleMessage("ホットキーなし"),
|
||||
"noIcon": MessageLookupByLibrary.simpleMessage("なし"),
|
||||
"noInfo": MessageLookupByLibrary.simpleMessage("情報なし"),
|
||||
"noLongerRemind": MessageLookupByLibrary.simpleMessage("今後表示しない"),
|
||||
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("追加情報なし"),
|
||||
"noNetwork": MessageLookupByLibrary.simpleMessage("ネットワークなし"),
|
||||
"noNetworkApp": MessageLookupByLibrary.simpleMessage("ネットワークなしアプリ"),
|
||||
@@ -398,8 +356,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"プロファイルがありません。追加してください",
|
||||
),
|
||||
"nullTip": m9,
|
||||
"numberTip": m10,
|
||||
"nullTip": m5,
|
||||
"numberTip": m6,
|
||||
"oneColumn": MessageLookupByLibrary.simpleMessage("1列"),
|
||||
"onlyIcon": MessageLookupByLibrary.simpleMessage("アイコンのみ"),
|
||||
"onlyOtherApps": MessageLookupByLibrary.simpleMessage("サードパーティアプリのみ"),
|
||||
@@ -420,9 +378,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
|
||||
"スクリプトモードでは有効になりません",
|
||||
),
|
||||
"overrideMode": MessageLookupByLibrary.simpleMessage("上書きモード"),
|
||||
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("元のルールを上書き"),
|
||||
"overrideScript": MessageLookupByLibrary.simpleMessage("上書きスクリプト"),
|
||||
"palette": MessageLookupByLibrary.simpleMessage("パレット"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("パスワード"),
|
||||
"paste": MessageLookupByLibrary.simpleMessage("貼り付け"),
|
||||
@@ -443,7 +399,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"port": MessageLookupByLibrary.simpleMessage("ポート"),
|
||||
"portConflictTip": MessageLookupByLibrary.simpleMessage("別のポートを入力してください"),
|
||||
"portTip": m11,
|
||||
"portTip": m7,
|
||||
"preferH3Desc": MessageLookupByLibrary.simpleMessage("DOHのHTTP/3を優先使用"),
|
||||
"pressKeyboard": MessageLookupByLibrary.simpleMessage("キーボードを押してください"),
|
||||
"preview": MessageLookupByLibrary.simpleMessage("プレビュー"),
|
||||
@@ -499,7 +455,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"redirPort": MessageLookupByLibrary.simpleMessage("Redirポート"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("やり直す"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("正規表現"),
|
||||
"reload": MessageLookupByLibrary.simpleMessage("リロード"),
|
||||
"remote": MessageLookupByLibrary.simpleMessage("リモート"),
|
||||
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"WebDAVにデータをバックアップ",
|
||||
@@ -514,9 +469,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"requests": MessageLookupByLibrary.simpleMessage("リクエスト"),
|
||||
"requestsDesc": MessageLookupByLibrary.simpleMessage("最近のリクエスト記録を表示"),
|
||||
"reset": MessageLookupByLibrary.simpleMessage("リセット"),
|
||||
"resetPageChangesTip": MessageLookupByLibrary.simpleMessage(
|
||||
"現在のページに変更があります。リセットしてもよろしいですか?",
|
||||
),
|
||||
"resetTip": MessageLookupByLibrary.simpleMessage("リセットを確定"),
|
||||
"resources": MessageLookupByLibrary.simpleMessage("リソース"),
|
||||
"resourcesDesc": MessageLookupByLibrary.simpleMessage("外部リソース関連情報"),
|
||||
@@ -524,7 +476,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS接続がルールに従う(proxy-server-nameserverの設定が必要)",
|
||||
),
|
||||
"restart": MessageLookupByLibrary.simpleMessage("再起動"),
|
||||
"restartCoreTip": MessageLookupByLibrary.simpleMessage("コアを再起動してもよろしいですか?"),
|
||||
"routeAddress": MessageLookupByLibrary.simpleMessage("ルートアドレス"),
|
||||
"routeAddressDesc": MessageLookupByLibrary.simpleMessage("ルートアドレスを設定"),
|
||||
@@ -542,14 +493,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"saveChanges": MessageLookupByLibrary.simpleMessage("変更を保存しますか?"),
|
||||
"saveTip": MessageLookupByLibrary.simpleMessage("保存してもよろしいですか?"),
|
||||
"script": MessageLookupByLibrary.simpleMessage("スクリプト"),
|
||||
"scriptModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"スクリプトモード、外部拡張スクリプトを使用し、ワンクリックで設定を上書きする機能を提供",
|
||||
),
|
||||
"search": MessageLookupByLibrary.simpleMessage("検索"),
|
||||
"seconds": MessageLookupByLibrary.simpleMessage("秒"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("すべて選択"),
|
||||
"selected": MessageLookupByLibrary.simpleMessage("選択済み"),
|
||||
"selectedCountTitle": m12,
|
||||
"selectedCountTitle": m8,
|
||||
"settings": MessageLookupByLibrary.simpleMessage("設定"),
|
||||
"show": MessageLookupByLibrary.simpleMessage("表示"),
|
||||
"shrink": MessageLookupByLibrary.simpleMessage("縮小"),
|
||||
@@ -562,12 +510,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sourceIp": MessageLookupByLibrary.simpleMessage("送信元IP"),
|
||||
"specialProxy": MessageLookupByLibrary.simpleMessage("特殊プロキシ"),
|
||||
"specialRules": MessageLookupByLibrary.simpleMessage("特殊ルール"),
|
||||
"speedStatistics": MessageLookupByLibrary.simpleMessage("速度統計"),
|
||||
"stackMode": MessageLookupByLibrary.simpleMessage("スタックモード"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("標準"),
|
||||
"standardModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"標準モード、基本設定を上書きし、シンプルなルール追加機能を提供",
|
||||
),
|
||||
"start": MessageLookupByLibrary.simpleMessage("開始"),
|
||||
"startVpn": MessageLookupByLibrary.simpleMessage("VPNを開始中..."),
|
||||
"status": MessageLookupByLibrary.simpleMessage("ステータス"),
|
||||
@@ -607,8 +551,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"trafficUsage": MessageLookupByLibrary.simpleMessage("トラフィック使用量"),
|
||||
"tun": MessageLookupByLibrary.simpleMessage("TUN"),
|
||||
"tunDesc": MessageLookupByLibrary.simpleMessage("管理者モードでのみ有効"),
|
||||
"turnOff": MessageLookupByLibrary.simpleMessage("オフ"),
|
||||
"turnOn": MessageLookupByLibrary.simpleMessage("オン"),
|
||||
"twoColumns": MessageLookupByLibrary.simpleMessage("2列"),
|
||||
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"現在のプロファイルを更新できません",
|
||||
@@ -624,15 +566,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"upload": MessageLookupByLibrary.simpleMessage("アップロード"),
|
||||
"url": MessageLookupByLibrary.simpleMessage("URL"),
|
||||
"urlDesc": MessageLookupByLibrary.simpleMessage("URL経由でプロファイルを取得"),
|
||||
"urlTip": m13,
|
||||
"urlTip": m9,
|
||||
"useHosts": MessageLookupByLibrary.simpleMessage("ホストを使用"),
|
||||
"useSystemHosts": MessageLookupByLibrary.simpleMessage("システムホストを使用"),
|
||||
"value": MessageLookupByLibrary.simpleMessage("値"),
|
||||
"vibrantScheme": MessageLookupByLibrary.simpleMessage("ビブラント"),
|
||||
"view": MessageLookupByLibrary.simpleMessage("表示"),
|
||||
"vpnConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
|
||||
"VPN設定の変更が検出されました",
|
||||
),
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage("VPN関連設定の変更"),
|
||||
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"VpnService経由で全システムトラフィックをルーティング",
|
||||
@@ -644,7 +583,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV設定"),
|
||||
"whitelistMode": MessageLookupByLibrary.simpleMessage("ホワイトリストモード"),
|
||||
"years": MessageLookupByLibrary.simpleMessage("年"),
|
||||
"yearsAgo": m14,
|
||||
"zh_CN": MessageLookupByLibrary.simpleMessage("簡体字中国語"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,41 +20,26 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'ru';
|
||||
|
||||
static String m0(count) =>
|
||||
"${Intl.plural(count, one: '${count} день назад', few: '${count} дня назад', many: '${count} дней назад', other: '${count} дня назад')}";
|
||||
|
||||
static String m1(label) =>
|
||||
static String m0(label) =>
|
||||
"Вы уверены, что хотите удалить выбранные ${label}?";
|
||||
|
||||
static String m2(label) => "Вы уверены, что хотите удалить текущий ${label}?";
|
||||
static String m1(label) => "Вы уверены, что хотите удалить текущий ${label}?";
|
||||
|
||||
static String m3(label) => "Детали {}";
|
||||
static String m2(label) => "Детали {}";
|
||||
|
||||
static String m4(label) => "${label} не может быть пустым";
|
||||
static String m3(label) => "${label} не может быть пустым";
|
||||
|
||||
static String m5(label) => "Текущий ${label} уже существует";
|
||||
static String m4(label) => "Текущий ${label} уже существует";
|
||||
|
||||
static String m6(count) =>
|
||||
"${Intl.plural(count, one: '${count} час назад', few: '${count} часа назад', many: '${count} часов назад', other: '${count} часа назад')}";
|
||||
static String m5(label) => "Сейчас ${label} нет";
|
||||
|
||||
static String m7(count) =>
|
||||
"${Intl.plural(count, one: '${count} минута назад', few: '${count} минуты назад', many: '${count} минут назад', other: '${count} минуты назад')}";
|
||||
static String m6(label) => "${label} должно быть числом";
|
||||
|
||||
static String m8(count) =>
|
||||
"${Intl.plural(count, one: '${count} месяц назад', few: '${count} месяца назад', many: '${count} месяцев назад', other: '${count} месяца назад')}";
|
||||
static String m7(label) => "${label} должен быть числом от 1024 до 49151";
|
||||
|
||||
static String m9(label) => "${label} пока отсутствуют";
|
||||
static String m8(count) => "Выбрано ${count} элементов";
|
||||
|
||||
static String m10(label) => "${label} должно быть числом";
|
||||
|
||||
static String m11(label) => "${label} должен быть числом от 1024 до 49151";
|
||||
|
||||
static String m12(count) => "Выбрано ${count} элементов";
|
||||
|
||||
static String m13(label) => "${label} должен быть URL";
|
||||
|
||||
static String m14(count) =>
|
||||
"${Intl.plural(count, one: '${count} год назад', few: '${count} года назад', many: '${count} лет назад', other: '${count} года назад')}";
|
||||
static String m9(label) => "${label} должен быть URL";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@@ -69,9 +54,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"accessControlNotAllowDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Выбранные приложения будут исключены из VPN",
|
||||
),
|
||||
"accessControlSettings": MessageLookupByLibrary.simpleMessage(
|
||||
"Настройки контроля доступа",
|
||||
),
|
||||
"account": MessageLookupByLibrary.simpleMessage("Аккаунт"),
|
||||
"action": MessageLookupByLibrary.simpleMessage("Действие"),
|
||||
"action_mode": MessageLookupByLibrary.simpleMessage("Переключить режим"),
|
||||
@@ -84,7 +66,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"addedOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Добавить к оригинальным правилам",
|
||||
),
|
||||
"addedRules": MessageLookupByLibrary.simpleMessage("Добавленные правила"),
|
||||
"address": MessageLookupByLibrary.simpleMessage("Адрес"),
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage("Адрес сервера WebDAV"),
|
||||
"addressTip": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -96,12 +77,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Запуск с правами администратора при загрузке системы",
|
||||
),
|
||||
"advancedConfig": MessageLookupByLibrary.simpleMessage(
|
||||
"Расширенная конфигурация",
|
||||
),
|
||||
"advancedConfigDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Предоставляет разнообразные варианты конфигурации",
|
||||
),
|
||||
"ago": MessageLookupByLibrary.simpleMessage(" назад"),
|
||||
"agree": MessageLookupByLibrary.simpleMessage("Согласен"),
|
||||
"allApps": MessageLookupByLibrary.simpleMessage("Все приложения"),
|
||||
@@ -122,12 +97,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"appDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Обработка настроек, связанных с приложением",
|
||||
),
|
||||
"appendSystemDns": MessageLookupByLibrary.simpleMessage(
|
||||
"Добавить системный DNS",
|
||||
),
|
||||
"appendSystemDnsTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Принудительно добавить системный DNS к конфигурации",
|
||||
),
|
||||
"application": MessageLookupByLibrary.simpleMessage("Приложение"),
|
||||
"applicationDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Изменение настроек, связанных с приложением",
|
||||
@@ -213,12 +182,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Включение приведет к потере части функциональности приложения, но обеспечит полную поддержку Clash.",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("Подтвердить"),
|
||||
"confirmClearAllData": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы уверены, что хотите очистить все данные?",
|
||||
),
|
||||
"confirmForceCrashCore": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы уверены, что хотите принудительно аварийно завершить работу ядра?",
|
||||
),
|
||||
"connected": MessageLookupByLibrary.simpleMessage("Подключено"),
|
||||
"connecting": MessageLookupByLibrary.simpleMessage("Подключение..."),
|
||||
"connection": MessageLookupByLibrary.simpleMessage("Соединение"),
|
||||
@@ -230,9 +193,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"contactMe": MessageLookupByLibrary.simpleMessage("Свяжитесь со мной"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("Содержание"),
|
||||
"contentScheme": MessageLookupByLibrary.simpleMessage("Контентная тема"),
|
||||
"controlGlobalAddedRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Управление глобальными добавленными правилами",
|
||||
),
|
||||
"copy": MessageLookupByLibrary.simpleMessage("Копировать"),
|
||||
"copyEnvVar": MessageLookupByLibrary.simpleMessage(
|
||||
"Копирование переменных окружения",
|
||||
@@ -240,9 +200,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"copyLink": MessageLookupByLibrary.simpleMessage("Копировать ссылку"),
|
||||
"copySuccess": MessageLookupByLibrary.simpleMessage("Копирование успешно"),
|
||||
"core": MessageLookupByLibrary.simpleMessage("Ядро"),
|
||||
"coreConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
|
||||
"Обнаружено изменение конфигурации ядра",
|
||||
),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("Информация о ядре"),
|
||||
"coreStatus": MessageLookupByLibrary.simpleMessage("Основной статус"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("Страна"),
|
||||
@@ -263,7 +220,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Уведомление о сборе данных",
|
||||
),
|
||||
"days": MessageLookupByLibrary.simpleMessage("Дней"),
|
||||
"daysAgo": m0,
|
||||
"defaultNameserver": MessageLookupByLibrary.simpleMessage(
|
||||
"Сервер имен по умолчанию",
|
||||
),
|
||||
@@ -277,8 +233,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"delay": MessageLookupByLibrary.simpleMessage("Задержка"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("Сортировка по задержке"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Удалить"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
"deleteMultipTip": m0,
|
||||
"deleteTip": m1,
|
||||
"desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Многоплатформенный прокси-клиент на основе ClashMeta, простой и удобный в использовании, с открытым исходным кодом и без рекламы.",
|
||||
),
|
||||
@@ -287,7 +243,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Геолокация назначения",
|
||||
),
|
||||
"destinationIPASN": MessageLookupByLibrary.simpleMessage("ASN назначения"),
|
||||
"details": m3,
|
||||
"details": m2,
|
||||
"detectionTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Опирается на сторонний API, только для справки",
|
||||
),
|
||||
@@ -320,11 +276,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"domain": MessageLookupByLibrary.simpleMessage("Домен"),
|
||||
"download": MessageLookupByLibrary.simpleMessage("Скачивание"),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("Редактировать"),
|
||||
"editGlobalRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Редактировать глобальные правила",
|
||||
),
|
||||
"editRule": MessageLookupByLibrary.simpleMessage("Редактировать правило"),
|
||||
"emptyTip": m4,
|
||||
"emptyTip": m3,
|
||||
"en": MessageLookupByLibrary.simpleMessage("Английский"),
|
||||
"enableOverride": MessageLookupByLibrary.simpleMessage(
|
||||
"Включить переопределение",
|
||||
@@ -336,7 +288,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"excludeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Когда приложение находится в фоновом режиме, оно скрыто из последних задач",
|
||||
),
|
||||
"existsTip": m5,
|
||||
"existsTip": m4,
|
||||
"exit": MessageLookupByLibrary.simpleMessage("Выход"),
|
||||
"expand": MessageLookupByLibrary.simpleMessage("Стандартный"),
|
||||
"expirationTime": MessageLookupByLibrary.simpleMessage("Время истечения"),
|
||||
@@ -350,7 +302,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"externalControllerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"При включении ядро Clash можно контролировать на порту 9090",
|
||||
),
|
||||
"externalFetch": MessageLookupByLibrary.simpleMessage("Внешнее получение"),
|
||||
"externalLink": MessageLookupByLibrary.simpleMessage("Внешняя ссылка"),
|
||||
"externalResources": MessageLookupByLibrary.simpleMessage(
|
||||
"Внешние ресурсы",
|
||||
@@ -403,9 +354,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"global": MessageLookupByLibrary.simpleMessage("Глобальный"),
|
||||
"go": MessageLookupByLibrary.simpleMessage("Перейти"),
|
||||
"goDownload": MessageLookupByLibrary.simpleMessage("Перейти к загрузке"),
|
||||
"goToConfigureScript": MessageLookupByLibrary.simpleMessage(
|
||||
"Перейти к настройке скрипта",
|
||||
),
|
||||
"hasCacheChange": MessageLookupByLibrary.simpleMessage(
|
||||
"Хотите сохранить изменения в кэше?",
|
||||
),
|
||||
@@ -421,7 +369,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Использование клавиатуры для управления приложением",
|
||||
),
|
||||
"hours": MessageLookupByLibrary.simpleMessage("Часов"),
|
||||
"hoursAgo": m6,
|
||||
"icon": MessageLookupByLibrary.simpleMessage("Иконка"),
|
||||
"iconConfiguration": MessageLookupByLibrary.simpleMessage(
|
||||
"Конфигурация иконки",
|
||||
@@ -453,7 +400,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"ja": MessageLookupByLibrary.simpleMessage("Японский"),
|
||||
"just": MessageLookupByLibrary.simpleMessage("Только что"),
|
||||
"justNow": MessageLookupByLibrary.simpleMessage("Только что"),
|
||||
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Интервал поддержания TCP-соединения",
|
||||
),
|
||||
@@ -463,8 +409,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"light": MessageLookupByLibrary.simpleMessage("Светлый"),
|
||||
"list": MessageLookupByLibrary.simpleMessage("Список"),
|
||||
"listen": MessageLookupByLibrary.simpleMessage("Слушать"),
|
||||
"loadTest": MessageLookupByLibrary.simpleMessage("Тест загрузки"),
|
||||
"loading": MessageLookupByLibrary.simpleMessage("Загрузка..."),
|
||||
"local": MessageLookupByLibrary.simpleMessage("Локальный"),
|
||||
"localBackupDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Резервное копирование локальных данных на локальный диск",
|
||||
@@ -501,12 +445,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Изменить стандартное событие выхода из системы",
|
||||
),
|
||||
"minutes": MessageLookupByLibrary.simpleMessage("Минут"),
|
||||
"minutesAgo": m7,
|
||||
"mixedPort": MessageLookupByLibrary.simpleMessage("Смешанный порт"),
|
||||
"mode": MessageLookupByLibrary.simpleMessage("Режим"),
|
||||
"monochromeScheme": MessageLookupByLibrary.simpleMessage("Монохром"),
|
||||
"months": MessageLookupByLibrary.simpleMessage("Месяцев"),
|
||||
"monthsAgo": m8,
|
||||
"more": MessageLookupByLibrary.simpleMessage("Еще"),
|
||||
"name": MessageLookupByLibrary.simpleMessage("Имя"),
|
||||
"nameSort": MessageLookupByLibrary.simpleMessage("Сортировка по имени"),
|
||||
@@ -534,9 +476,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noHotKey": MessageLookupByLibrary.simpleMessage("Нет горячей клавиши"),
|
||||
"noIcon": MessageLookupByLibrary.simpleMessage("Нет иконки"),
|
||||
"noInfo": MessageLookupByLibrary.simpleMessage("Нет информации"),
|
||||
"noLongerRemind": MessageLookupByLibrary.simpleMessage(
|
||||
"Больше не напоминать",
|
||||
),
|
||||
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Нет дополнительной информации",
|
||||
),
|
||||
@@ -554,8 +493,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Нет профиля, пожалуйста, добавьте профиль",
|
||||
),
|
||||
"nullTip": m9,
|
||||
"numberTip": m10,
|
||||
"nullTip": m5,
|
||||
"numberTip": m6,
|
||||
"oneColumn": MessageLookupByLibrary.simpleMessage("Один столбец"),
|
||||
"onlyIcon": MessageLookupByLibrary.simpleMessage("Только иконка"),
|
||||
"onlyOtherApps": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -586,15 +525,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
|
||||
"В скриптовом режиме не действует",
|
||||
),
|
||||
"overrideMode": MessageLookupByLibrary.simpleMessage(
|
||||
"Режим переопределения",
|
||||
),
|
||||
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Переопределить оригинальное правило",
|
||||
),
|
||||
"overrideScript": MessageLookupByLibrary.simpleMessage(
|
||||
"Скрипт переопределения",
|
||||
),
|
||||
"palette": MessageLookupByLibrary.simpleMessage("Палитра"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
|
||||
"paste": MessageLookupByLibrary.simpleMessage("Вставить"),
|
||||
@@ -617,7 +550,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"portConflictTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Введите другой порт",
|
||||
),
|
||||
"portTip": m11,
|
||||
"portTip": m7,
|
||||
"preferH3Desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Приоритетное использование HTTP/3 для DOH",
|
||||
),
|
||||
@@ -697,7 +630,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"redirPort": MessageLookupByLibrary.simpleMessage("Redir-порт"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("Повторить"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("Регулярное выражение"),
|
||||
"reload": MessageLookupByLibrary.simpleMessage("Перезагрузить"),
|
||||
"remote": MessageLookupByLibrary.simpleMessage("Удаленный"),
|
||||
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Резервное копирование локальных данных на WebDAV",
|
||||
@@ -716,9 +648,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Просмотр последних записей запросов",
|
||||
),
|
||||
"reset": MessageLookupByLibrary.simpleMessage("Сброс"),
|
||||
"resetPageChangesTip": MessageLookupByLibrary.simpleMessage(
|
||||
"На текущей странице есть изменения. Вы уверены, что хотите сбросить?",
|
||||
),
|
||||
"resetTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Убедитесь, что хотите сбросить",
|
||||
),
|
||||
@@ -730,7 +659,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS-соединение следует правилам, необходимо настроить proxy-server-nameserver",
|
||||
),
|
||||
"restart": MessageLookupByLibrary.simpleMessage("Перезапустить"),
|
||||
"restartCoreTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы уверены, что хотите перезапустить ядро?",
|
||||
),
|
||||
@@ -756,14 +684,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Вы уверены, что хотите сохранить?",
|
||||
),
|
||||
"script": MessageLookupByLibrary.simpleMessage("Скрипт"),
|
||||
"scriptModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Режим скрипта, использование внешних расширяющих скриптов, предоставление возможности переопределения конфигурации одним кликом",
|
||||
),
|
||||
"search": MessageLookupByLibrary.simpleMessage("Поиск"),
|
||||
"seconds": MessageLookupByLibrary.simpleMessage("Секунд"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("Выбрать все"),
|
||||
"selected": MessageLookupByLibrary.simpleMessage("Выбрано"),
|
||||
"selectedCountTitle": m12,
|
||||
"selectedCountTitle": m8,
|
||||
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
|
||||
"show": MessageLookupByLibrary.simpleMessage("Показать"),
|
||||
"shrink": MessageLookupByLibrary.simpleMessage("Сжать"),
|
||||
@@ -778,14 +703,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sourceIp": MessageLookupByLibrary.simpleMessage("Исходный IP"),
|
||||
"specialProxy": MessageLookupByLibrary.simpleMessage("Специальный прокси"),
|
||||
"specialRules": MessageLookupByLibrary.simpleMessage("Специальные правила"),
|
||||
"speedStatistics": MessageLookupByLibrary.simpleMessage(
|
||||
"Статистика скорости",
|
||||
),
|
||||
"stackMode": MessageLookupByLibrary.simpleMessage("Режим стека"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("Стандартный"),
|
||||
"standardModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Стандартный режим, переопределение базовой конфигурации, предоставление возможности простого добавления правил",
|
||||
),
|
||||
"start": MessageLookupByLibrary.simpleMessage("Старт"),
|
||||
"startVpn": MessageLookupByLibrary.simpleMessage("Запуск VPN..."),
|
||||
"status": MessageLookupByLibrary.simpleMessage("Статус"),
|
||||
@@ -837,8 +756,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tunDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"действительно только в режиме администратора",
|
||||
),
|
||||
"turnOff": MessageLookupByLibrary.simpleMessage("Выключить"),
|
||||
"turnOn": MessageLookupByLibrary.simpleMessage("Включить"),
|
||||
"twoColumns": MessageLookupByLibrary.simpleMessage("Два столбца"),
|
||||
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"невозможно обновить текущий профиль",
|
||||
@@ -858,7 +775,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"urlDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Получить профиль через URL",
|
||||
),
|
||||
"urlTip": m13,
|
||||
"urlTip": m9,
|
||||
"useHosts": MessageLookupByLibrary.simpleMessage("Использовать hosts"),
|
||||
"useSystemHosts": MessageLookupByLibrary.simpleMessage(
|
||||
"Использовать системные hosts",
|
||||
@@ -866,9 +783,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"value": MessageLookupByLibrary.simpleMessage("Значение"),
|
||||
"vibrantScheme": MessageLookupByLibrary.simpleMessage("Яркие"),
|
||||
"view": MessageLookupByLibrary.simpleMessage("Просмотр"),
|
||||
"vpnConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
|
||||
"Обнаружено изменение конфигурации VPN",
|
||||
),
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Изменение настроек, связанных с VPN",
|
||||
),
|
||||
@@ -888,7 +802,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Режим белого списка",
|
||||
),
|
||||
"years": MessageLookupByLibrary.simpleMessage("Лет"),
|
||||
"yearsAgo": m14,
|
||||
"zh_CN": MessageLookupByLibrary.simpleMessage("Упрощенный китайский"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,35 +20,25 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'zh_CN';
|
||||
|
||||
static String m0(count) => "${count} 天前";
|
||||
static String m0(label) => "确定删除选中的${label}吗?";
|
||||
|
||||
static String m1(label) => "确定删除选中的${label}吗?";
|
||||
static String m1(label) => "确定删除当前${label}吗?";
|
||||
|
||||
static String m2(label) => "确定删除当前${label}吗?";
|
||||
static String m2(label) => "${label}详情";
|
||||
|
||||
static String m3(label) => "${label}详情";
|
||||
static String m3(label) => "${label}不能为空";
|
||||
|
||||
static String m4(label) => "${label}不能为空";
|
||||
static String m4(label) => "${label}当前已存在";
|
||||
|
||||
static String m5(label) => "${label}当前已存在";
|
||||
static String m5(label) => "暂无${label}";
|
||||
|
||||
static String m6(count) => "${count} 小时前";
|
||||
static String m6(label) => "${label}必须为数字";
|
||||
|
||||
static String m7(count) => "${count} 分钟前";
|
||||
static String m7(label) => "${label} 必须在 1024 到 49151 之间";
|
||||
|
||||
static String m8(count) => "${count} 个月前";
|
||||
static String m8(count) => "已选择 ${count} 项";
|
||||
|
||||
static String m9(label) => "暂无${label}";
|
||||
|
||||
static String m10(label) => "${label}必须为数字";
|
||||
|
||||
static String m11(label) => "${label} 必须在 1024 到 49151 之间";
|
||||
|
||||
static String m12(count) => "已选择 ${count} 项";
|
||||
|
||||
static String m13(label) => "${label}必须为URL";
|
||||
|
||||
static String m14(count) => "${count} 年前";
|
||||
static String m9(label) => "${label}必须为URL";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@@ -61,7 +51,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"accessControlNotAllowDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"选中应用将会被排除在VPN之外",
|
||||
),
|
||||
"accessControlSettings": MessageLookupByLibrary.simpleMessage("访问控制设置"),
|
||||
"account": MessageLookupByLibrary.simpleMessage("账号"),
|
||||
"action": MessageLookupByLibrary.simpleMessage("操作"),
|
||||
"action_mode": MessageLookupByLibrary.simpleMessage("切换模式"),
|
||||
@@ -72,14 +61,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"add": MessageLookupByLibrary.simpleMessage("添加"),
|
||||
"addRule": MessageLookupByLibrary.simpleMessage("添加规则"),
|
||||
"addedOriginRules": MessageLookupByLibrary.simpleMessage("附加到原始规则"),
|
||||
"addedRules": MessageLookupByLibrary.simpleMessage("附加规则"),
|
||||
"address": MessageLookupByLibrary.simpleMessage("地址"),
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAV服务器地址"),
|
||||
"addressTip": MessageLookupByLibrary.simpleMessage("请输入有效的WebDAV地址"),
|
||||
"adminAutoLaunch": MessageLookupByLibrary.simpleMessage("管理员自启动"),
|
||||
"adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage("使用管理员模式开机自启动"),
|
||||
"advancedConfig": MessageLookupByLibrary.simpleMessage("进阶配置"),
|
||||
"advancedConfigDesc": MessageLookupByLibrary.simpleMessage("提供多样化配置"),
|
||||
"ago": MessageLookupByLibrary.simpleMessage("前"),
|
||||
"agree": MessageLookupByLibrary.simpleMessage("同意"),
|
||||
"allApps": MessageLookupByLibrary.simpleMessage("所有应用"),
|
||||
@@ -90,8 +76,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"app": MessageLookupByLibrary.simpleMessage("应用"),
|
||||
"appAccessControl": MessageLookupByLibrary.simpleMessage("应用访问控制"),
|
||||
"appDesc": MessageLookupByLibrary.simpleMessage("处理应用相关设置"),
|
||||
"appendSystemDns": MessageLookupByLibrary.simpleMessage("追加系统DNS"),
|
||||
"appendSystemDnsTip": MessageLookupByLibrary.simpleMessage("强制为配置附加系统DNS"),
|
||||
"application": MessageLookupByLibrary.simpleMessage("应用程序"),
|
||||
"applicationDesc": MessageLookupByLibrary.simpleMessage("修改应用程序相关设置"),
|
||||
"auto": MessageLookupByLibrary.simpleMessage("自动"),
|
||||
@@ -139,8 +123,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"开启将失去部分应用能力,获得全量的Clash的支持",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("确定"),
|
||||
"confirmClearAllData": MessageLookupByLibrary.simpleMessage("确定要清除所有数据?"),
|
||||
"confirmForceCrashCore": MessageLookupByLibrary.simpleMessage("确定要强制崩溃核心?"),
|
||||
"connected": MessageLookupByLibrary.simpleMessage("已连接"),
|
||||
"connecting": MessageLookupByLibrary.simpleMessage("连接中..."),
|
||||
"connection": MessageLookupByLibrary.simpleMessage("连接"),
|
||||
@@ -150,15 +132,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"contactMe": MessageLookupByLibrary.simpleMessage("联系我"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("内容"),
|
||||
"contentScheme": MessageLookupByLibrary.simpleMessage("内容主题"),
|
||||
"controlGlobalAddedRules": MessageLookupByLibrary.simpleMessage("控制全局附加规则"),
|
||||
"copy": MessageLookupByLibrary.simpleMessage("复制"),
|
||||
"copyEnvVar": MessageLookupByLibrary.simpleMessage("复制环境变量"),
|
||||
"copyLink": MessageLookupByLibrary.simpleMessage("复制链接"),
|
||||
"copySuccess": MessageLookupByLibrary.simpleMessage("复制成功"),
|
||||
"core": MessageLookupByLibrary.simpleMessage("内核"),
|
||||
"coreConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
|
||||
"检测到核心配置更改",
|
||||
),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
|
||||
"coreStatus": MessageLookupByLibrary.simpleMessage("核心状态"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("区域"),
|
||||
@@ -177,7 +155,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"dataCollectionTip": MessageLookupByLibrary.simpleMessage("数据收集说明"),
|
||||
"days": MessageLookupByLibrary.simpleMessage("天"),
|
||||
"daysAgo": m0,
|
||||
"defaultNameserver": MessageLookupByLibrary.simpleMessage("默认域名服务器"),
|
||||
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析DNS服务器"),
|
||||
"defaultSort": MessageLookupByLibrary.simpleMessage("按默认排序"),
|
||||
@@ -185,15 +162,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"delay": MessageLookupByLibrary.simpleMessage("延迟"),
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("删除"),
|
||||
"deleteMultipTip": m1,
|
||||
"deleteTip": m2,
|
||||
"deleteMultipTip": m0,
|
||||
"deleteTip": m1,
|
||||
"desc": MessageLookupByLibrary.simpleMessage(
|
||||
"基于ClashMeta的多平台代理客户端,简单易用,开源无广告。",
|
||||
),
|
||||
"destination": MessageLookupByLibrary.simpleMessage("目标地址"),
|
||||
"destinationGeoIP": MessageLookupByLibrary.simpleMessage("目标地理定位"),
|
||||
"destinationIPASN": MessageLookupByLibrary.simpleMessage("目标IP ASN"),
|
||||
"details": m3,
|
||||
"details": m2,
|
||||
"detectionTip": MessageLookupByLibrary.simpleMessage("依赖第三方api,仅供参考"),
|
||||
"developerMode": MessageLookupByLibrary.simpleMessage("开发者模式"),
|
||||
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage("开发者模式已启用。"),
|
||||
@@ -212,15 +189,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"domain": MessageLookupByLibrary.simpleMessage("域名"),
|
||||
"download": MessageLookupByLibrary.simpleMessage("下载"),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
|
||||
"editGlobalRules": MessageLookupByLibrary.simpleMessage("编辑全局规则"),
|
||||
"editRule": MessageLookupByLibrary.simpleMessage("编辑规则"),
|
||||
"emptyTip": m4,
|
||||
"emptyTip": m3,
|
||||
"en": MessageLookupByLibrary.simpleMessage("英语"),
|
||||
"enableOverride": MessageLookupByLibrary.simpleMessage("启用覆写"),
|
||||
"entries": MessageLookupByLibrary.simpleMessage("个条目"),
|
||||
"exclude": MessageLookupByLibrary.simpleMessage("从最近任务中隐藏"),
|
||||
"excludeDesc": MessageLookupByLibrary.simpleMessage("应用在后台时,从最近任务中隐藏应用"),
|
||||
"existsTip": m5,
|
||||
"existsTip": m4,
|
||||
"exit": MessageLookupByLibrary.simpleMessage("退出"),
|
||||
"expand": MessageLookupByLibrary.simpleMessage("标准"),
|
||||
"expirationTime": MessageLookupByLibrary.simpleMessage("到期时间"),
|
||||
@@ -232,7 +207,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"externalControllerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"开启后将可以通过9090端口控制Clash内核",
|
||||
),
|
||||
"externalFetch": MessageLookupByLibrary.simpleMessage("外部获取"),
|
||||
"externalLink": MessageLookupByLibrary.simpleMessage("外部链接"),
|
||||
"externalResources": MessageLookupByLibrary.simpleMessage("外部资源"),
|
||||
"fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip过滤"),
|
||||
@@ -261,7 +235,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"global": MessageLookupByLibrary.simpleMessage("全局"),
|
||||
"go": MessageLookupByLibrary.simpleMessage("前往"),
|
||||
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
|
||||
"goToConfigureScript": MessageLookupByLibrary.simpleMessage("前往配置脚本"),
|
||||
"hasCacheChange": MessageLookupByLibrary.simpleMessage("是否缓存修改"),
|
||||
"host": MessageLookupByLibrary.simpleMessage("主机"),
|
||||
"hostsDesc": MessageLookupByLibrary.simpleMessage("追加Hosts"),
|
||||
@@ -269,7 +242,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"hotkeyManagement": MessageLookupByLibrary.simpleMessage("快捷键管理"),
|
||||
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage("使用键盘控制应用程序"),
|
||||
"hours": MessageLookupByLibrary.simpleMessage("小时"),
|
||||
"hoursAgo": m6,
|
||||
"icon": MessageLookupByLibrary.simpleMessage("图片"),
|
||||
"iconConfiguration": MessageLookupByLibrary.simpleMessage("图片配置"),
|
||||
"iconStyle": MessageLookupByLibrary.simpleMessage("图标样式"),
|
||||
@@ -289,7 +261,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"ipv6InboundDesc": MessageLookupByLibrary.simpleMessage("允许IPv6入站"),
|
||||
"ja": MessageLookupByLibrary.simpleMessage("日语"),
|
||||
"just": MessageLookupByLibrary.simpleMessage("刚刚"),
|
||||
"justNow": MessageLookupByLibrary.simpleMessage("刚刚"),
|
||||
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"),
|
||||
"key": MessageLookupByLibrary.simpleMessage("键"),
|
||||
"language": MessageLookupByLibrary.simpleMessage("语言"),
|
||||
@@ -297,8 +268,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"light": MessageLookupByLibrary.simpleMessage("浅色"),
|
||||
"list": MessageLookupByLibrary.simpleMessage("列表"),
|
||||
"listen": MessageLookupByLibrary.simpleMessage("监听"),
|
||||
"loadTest": MessageLookupByLibrary.simpleMessage("加载测试"),
|
||||
"loading": MessageLookupByLibrary.simpleMessage("加载中..."),
|
||||
"local": MessageLookupByLibrary.simpleMessage("本地"),
|
||||
"localBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到本地"),
|
||||
"localRecoveryDesc": MessageLookupByLibrary.simpleMessage("通过文件恢复数据"),
|
||||
@@ -319,12 +288,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"),
|
||||
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage("修改系统默认退出事件"),
|
||||
"minutes": MessageLookupByLibrary.simpleMessage("分钟"),
|
||||
"minutesAgo": m7,
|
||||
"mixedPort": MessageLookupByLibrary.simpleMessage("混合端口"),
|
||||
"mode": MessageLookupByLibrary.simpleMessage("模式"),
|
||||
"monochromeScheme": MessageLookupByLibrary.simpleMessage("单色"),
|
||||
"months": MessageLookupByLibrary.simpleMessage("月"),
|
||||
"monthsAgo": m8,
|
||||
"more": MessageLookupByLibrary.simpleMessage("更多"),
|
||||
"name": MessageLookupByLibrary.simpleMessage("名称"),
|
||||
"nameSort": MessageLookupByLibrary.simpleMessage("按名称排序"),
|
||||
@@ -342,7 +309,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noHotKey": MessageLookupByLibrary.simpleMessage("暂无快捷键"),
|
||||
"noIcon": MessageLookupByLibrary.simpleMessage("无图标"),
|
||||
"noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"),
|
||||
"noLongerRemind": MessageLookupByLibrary.simpleMessage("不再提示"),
|
||||
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("暂无更多信息"),
|
||||
"noNetwork": MessageLookupByLibrary.simpleMessage("无网络"),
|
||||
"noNetworkApp": MessageLookupByLibrary.simpleMessage("无网络应用"),
|
||||
@@ -352,8 +318,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"none": MessageLookupByLibrary.simpleMessage("无"),
|
||||
"notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"),
|
||||
"nullProfileDesc": MessageLookupByLibrary.simpleMessage("没有配置文件,请先添加配置文件"),
|
||||
"nullTip": m9,
|
||||
"numberTip": m10,
|
||||
"nullTip": m5,
|
||||
"numberTip": m6,
|
||||
"oneColumn": MessageLookupByLibrary.simpleMessage("一列"),
|
||||
"onlyIcon": MessageLookupByLibrary.simpleMessage("仅图标"),
|
||||
"onlyOtherApps": MessageLookupByLibrary.simpleMessage("仅第三方应用"),
|
||||
@@ -370,9 +336,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"overrideDns": MessageLookupByLibrary.simpleMessage("覆写DNS"),
|
||||
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage("开启后将覆盖配置中的DNS选项"),
|
||||
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage("在脚本模式下不生效"),
|
||||
"overrideMode": MessageLookupByLibrary.simpleMessage("覆写模式"),
|
||||
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("覆盖原始规则"),
|
||||
"overrideScript": MessageLookupByLibrary.simpleMessage("覆写脚本"),
|
||||
"palette": MessageLookupByLibrary.simpleMessage("调色板"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("密码"),
|
||||
"paste": MessageLookupByLibrary.simpleMessage("粘贴"),
|
||||
@@ -387,7 +351,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"port": MessageLookupByLibrary.simpleMessage("端口"),
|
||||
"portConflictTip": MessageLookupByLibrary.simpleMessage("请输入不同的端口"),
|
||||
"portTip": m11,
|
||||
"portTip": m7,
|
||||
"preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"),
|
||||
"pressKeyboard": MessageLookupByLibrary.simpleMessage("请按下按键"),
|
||||
"preview": MessageLookupByLibrary.simpleMessage("预览"),
|
||||
@@ -437,7 +401,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"redirPort": MessageLookupByLibrary.simpleMessage("Redir端口"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("重做"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("正则"),
|
||||
"reload": MessageLookupByLibrary.simpleMessage("重载"),
|
||||
"remote": MessageLookupByLibrary.simpleMessage("远程"),
|
||||
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到WebDAV"),
|
||||
"remoteDestination": MessageLookupByLibrary.simpleMessage("远程目标"),
|
||||
@@ -448,9 +411,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"requests": MessageLookupByLibrary.simpleMessage("请求"),
|
||||
"requestsDesc": MessageLookupByLibrary.simpleMessage("查看最近请求记录"),
|
||||
"reset": MessageLookupByLibrary.simpleMessage("重置"),
|
||||
"resetPageChangesTip": MessageLookupByLibrary.simpleMessage(
|
||||
"当前页面存在更改,确定重置吗?",
|
||||
),
|
||||
"resetTip": MessageLookupByLibrary.simpleMessage("确定要重置吗?"),
|
||||
"resources": MessageLookupByLibrary.simpleMessage("资源"),
|
||||
"resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"),
|
||||
@@ -458,7 +418,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS连接跟随rules,需配置proxy-server-nameserver",
|
||||
),
|
||||
"restart": MessageLookupByLibrary.simpleMessage("重启"),
|
||||
"restartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要重启核心吗?"),
|
||||
"routeAddress": MessageLookupByLibrary.simpleMessage("路由地址"),
|
||||
"routeAddressDesc": MessageLookupByLibrary.simpleMessage("配置监听路由地址"),
|
||||
@@ -474,14 +433,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"saveChanges": MessageLookupByLibrary.simpleMessage("是否保存更改?"),
|
||||
"saveTip": MessageLookupByLibrary.simpleMessage("确定要保存吗?"),
|
||||
"script": MessageLookupByLibrary.simpleMessage("脚本"),
|
||||
"scriptModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"脚本模式,使用外部扩展脚本,提供一键覆写配置的能力",
|
||||
),
|
||||
"search": MessageLookupByLibrary.simpleMessage("搜索"),
|
||||
"seconds": MessageLookupByLibrary.simpleMessage("秒"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
|
||||
"selected": MessageLookupByLibrary.simpleMessage("已选择"),
|
||||
"selectedCountTitle": m12,
|
||||
"selectedCountTitle": m8,
|
||||
"settings": MessageLookupByLibrary.simpleMessage("设置"),
|
||||
"show": MessageLookupByLibrary.simpleMessage("显示"),
|
||||
"shrink": MessageLookupByLibrary.simpleMessage("紧凑"),
|
||||
@@ -494,12 +450,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sourceIp": MessageLookupByLibrary.simpleMessage("源IP"),
|
||||
"specialProxy": MessageLookupByLibrary.simpleMessage("特殊代理"),
|
||||
"specialRules": MessageLookupByLibrary.simpleMessage("特殊规则"),
|
||||
"speedStatistics": MessageLookupByLibrary.simpleMessage("网速统计"),
|
||||
"stackMode": MessageLookupByLibrary.simpleMessage("栈模式"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("标准"),
|
||||
"standardModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"标准模式,覆写基本配置,提供简单追加规则能力",
|
||||
),
|
||||
"start": MessageLookupByLibrary.simpleMessage("启动"),
|
||||
"startVpn": MessageLookupByLibrary.simpleMessage("正在启动VPN..."),
|
||||
"status": MessageLookupByLibrary.simpleMessage("状态"),
|
||||
@@ -537,8 +489,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"trafficUsage": MessageLookupByLibrary.simpleMessage("流量统计"),
|
||||
"tun": MessageLookupByLibrary.simpleMessage("虚拟网卡"),
|
||||
"tunDesc": MessageLookupByLibrary.simpleMessage("仅在管理员模式生效"),
|
||||
"turnOff": MessageLookupByLibrary.simpleMessage("关闭"),
|
||||
"turnOn": MessageLookupByLibrary.simpleMessage("开启"),
|
||||
"twoColumns": MessageLookupByLibrary.simpleMessage("两列"),
|
||||
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"无法更新当前配置文件",
|
||||
@@ -552,15 +502,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"upload": MessageLookupByLibrary.simpleMessage("上传"),
|
||||
"url": MessageLookupByLibrary.simpleMessage("URL"),
|
||||
"urlDesc": MessageLookupByLibrary.simpleMessage("通过URL获取配置文件"),
|
||||
"urlTip": m13,
|
||||
"urlTip": m9,
|
||||
"useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"),
|
||||
"useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"),
|
||||
"value": MessageLookupByLibrary.simpleMessage("值"),
|
||||
"vibrantScheme": MessageLookupByLibrary.simpleMessage("活力"),
|
||||
"view": MessageLookupByLibrary.simpleMessage("查看"),
|
||||
"vpnConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
|
||||
"检测到VPN相关配置改动",
|
||||
),
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"),
|
||||
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"通过VpnService自动路由系统所有流量",
|
||||
@@ -572,7 +519,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV配置"),
|
||||
"whitelistMode": MessageLookupByLibrary.simpleMessage("白名单模式"),
|
||||
"years": MessageLookupByLibrary.simpleMessage("年"),
|
||||
"yearsAgo": m14,
|
||||
"zh_CN": MessageLookupByLibrary.simpleMessage("中文简体"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2619,26 +2619,6 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Advanced configuration`
|
||||
String get advancedConfig {
|
||||
return Intl.message(
|
||||
'Advanced configuration',
|
||||
name: 'advancedConfig',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Provide diverse configuration options`
|
||||
String get advancedConfigDesc {
|
||||
return Intl.message(
|
||||
'Provide diverse configuration options',
|
||||
name: 'advancedConfigDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `{count} items have been selected`
|
||||
String selectedCountTitle(Object count) {
|
||||
return Intl.message(
|
||||
@@ -3034,10 +3014,10 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `No {label} yet`
|
||||
/// `No {label} at the moment`
|
||||
String nullTip(Object label) {
|
||||
return Intl.message(
|
||||
'No $label yet',
|
||||
'No $label at the moment',
|
||||
name: 'nullTip',
|
||||
desc: '',
|
||||
args: [label],
|
||||
@@ -3378,291 +3358,6 @@ class AppLocalizations {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Append System DNS`
|
||||
String get appendSystemDns {
|
||||
return Intl.message(
|
||||
'Append System DNS',
|
||||
name: 'appendSystemDns',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Forcefully append system DNS to the configuration`
|
||||
String get appendSystemDnsTip {
|
||||
return Intl.message(
|
||||
'Forcefully append system DNS to the configuration',
|
||||
name: 'appendSystemDnsTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Edit rule`
|
||||
String get editRule {
|
||||
return Intl.message('Edit rule', name: 'editRule', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Override mode`
|
||||
String get overrideMode {
|
||||
return Intl.message(
|
||||
'Override mode',
|
||||
name: 'overrideMode',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Standard mode, override basic configuration, provide simple rule addition capability`
|
||||
String get standardModeDesc {
|
||||
return Intl.message(
|
||||
'Standard mode, override basic configuration, provide simple rule addition capability',
|
||||
name: 'standardModeDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Script mode, use external extension scripts, provide one-click override configuration capability`
|
||||
String get scriptModeDesc {
|
||||
return Intl.message(
|
||||
'Script mode, use external extension scripts, provide one-click override configuration capability',
|
||||
name: 'scriptModeDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Added rules`
|
||||
String get addedRules {
|
||||
return Intl.message('Added rules', name: 'addedRules', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Control global added rules`
|
||||
String get controlGlobalAddedRules {
|
||||
return Intl.message(
|
||||
'Control global added rules',
|
||||
name: 'controlGlobalAddedRules',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Override script`
|
||||
String get overrideScript {
|
||||
return Intl.message(
|
||||
'Override script',
|
||||
name: 'overrideScript',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Go to configure script`
|
||||
String get goToConfigureScript {
|
||||
return Intl.message(
|
||||
'Go to configure script',
|
||||
name: 'goToConfigureScript',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Edit global rules`
|
||||
String get editGlobalRules {
|
||||
return Intl.message(
|
||||
'Edit global rules',
|
||||
name: 'editGlobalRules',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `External fetch`
|
||||
String get externalFetch {
|
||||
return Intl.message(
|
||||
'External fetch',
|
||||
name: 'externalFetch',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Are you sure you want to force crash the core?`
|
||||
String get confirmForceCrashCore {
|
||||
return Intl.message(
|
||||
'Are you sure you want to force crash the core?',
|
||||
name: 'confirmForceCrashCore',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Are you sure you want to clear all data?`
|
||||
String get confirmClearAllData {
|
||||
return Intl.message(
|
||||
'Are you sure you want to clear all data?',
|
||||
name: 'confirmClearAllData',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Loading...`
|
||||
String get loading {
|
||||
return Intl.message('Loading...', name: 'loading', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Load test`
|
||||
String get loadTest {
|
||||
return Intl.message('Load test', name: 'loadTest', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `{count, plural, =1{1 year ago} other{{count} years ago}}`
|
||||
String yearsAgo(num count) {
|
||||
return Intl.plural(
|
||||
count,
|
||||
one: '1 year ago',
|
||||
other: '$count years ago',
|
||||
name: 'yearsAgo',
|
||||
desc: '',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
|
||||
/// `{count, plural, =1{1 month ago} other{{count} months ago}}`
|
||||
String monthsAgo(num count) {
|
||||
return Intl.plural(
|
||||
count,
|
||||
one: '1 month ago',
|
||||
other: '$count months ago',
|
||||
name: 'monthsAgo',
|
||||
desc: '',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
|
||||
/// `{count, plural, =1{1 day ago} other{{count} days ago}}`
|
||||
String daysAgo(num count) {
|
||||
return Intl.plural(
|
||||
count,
|
||||
one: '1 day ago',
|
||||
other: '$count days ago',
|
||||
name: 'daysAgo',
|
||||
desc: '',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
|
||||
/// `{count, plural, =1{1 hour ago} other{{count} hours ago}}`
|
||||
String hoursAgo(num count) {
|
||||
return Intl.plural(
|
||||
count,
|
||||
one: '1 hour ago',
|
||||
other: '$count hours ago',
|
||||
name: 'hoursAgo',
|
||||
desc: '',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
|
||||
/// `{count, plural, =1{1 minute ago} other{{count} minutes ago}}`
|
||||
String minutesAgo(num count) {
|
||||
return Intl.plural(
|
||||
count,
|
||||
one: '1 minute ago',
|
||||
other: '$count minutes ago',
|
||||
name: 'minutesAgo',
|
||||
desc: '',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Just now`
|
||||
String get justNow {
|
||||
return Intl.message('Just now', name: 'justNow', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Don't remind again`
|
||||
String get noLongerRemind {
|
||||
return Intl.message(
|
||||
'Don\'t remind again',
|
||||
name: 'noLongerRemind',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Access Control Settings`
|
||||
String get accessControlSettings {
|
||||
return Intl.message(
|
||||
'Access Control Settings',
|
||||
name: 'accessControlSettings',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Turn On`
|
||||
String get turnOn {
|
||||
return Intl.message('Turn On', name: 'turnOn', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Turn Off`
|
||||
String get turnOff {
|
||||
return Intl.message('Turn Off', name: 'turnOff', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Core configuration change detected`
|
||||
String get coreConfigChangeDetected {
|
||||
return Intl.message(
|
||||
'Core configuration change detected',
|
||||
name: 'coreConfigChangeDetected',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Reload`
|
||||
String get reload {
|
||||
return Intl.message('Reload', name: 'reload', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `VPN configuration change detected`
|
||||
String get vpnConfigChangeDetected {
|
||||
return Intl.message(
|
||||
'VPN configuration change detected',
|
||||
name: 'vpnConfigChangeDetected',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Restart`
|
||||
String get restart {
|
||||
return Intl.message('Restart', name: 'restart', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Speed statistics`
|
||||
String get speedStatistics {
|
||||
return Intl.message(
|
||||
'Speed statistics',
|
||||
name: 'speedStatistics',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `The current page has changes. Are you sure you want to reset?`
|
||||
String get resetPageChangesTip {
|
||||
return Intl.message(
|
||||
'The current page has changes. Are you sure you want to reset?',
|
||||
name: 'resetPageChangesTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -22,7 +22,6 @@ Future<void> main() async {
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _service(List<String> flags) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
globalState.isService = true;
|
||||
await globalState.init();
|
||||
await coreController.preload();
|
||||
tile?.addListener(
|
||||
@@ -33,22 +32,16 @@ Future<void> _service(List<String> flags) async {
|
||||
},
|
||||
),
|
||||
);
|
||||
app?.tip(appLocalizations.startVpn);
|
||||
final version = await system.version;
|
||||
await coreController.init(version);
|
||||
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
|
||||
enable: false,
|
||||
);
|
||||
final setupState = globalState.getSetupState(
|
||||
globalState.config.currentProfileId,
|
||||
);
|
||||
globalState.setupConfig(
|
||||
setupState: setupState,
|
||||
patchConfig: clashConfig,
|
||||
preloadInvoke: () {
|
||||
globalState.handleStart();
|
||||
},
|
||||
);
|
||||
Future(() async {
|
||||
app?.tip(appLocalizations.startVpn);
|
||||
final version = await system.version;
|
||||
await coreController.init(version);
|
||||
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
|
||||
enable: false,
|
||||
);
|
||||
await globalState.handleStart();
|
||||
await coreController.setupConfig(clashConfig);
|
||||
});
|
||||
}
|
||||
|
||||
@immutable
|
||||
|
||||
@@ -71,16 +71,16 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
|
||||
@override
|
||||
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||
commonPrint.log('$state');
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
if (state == AppLifecycleState.paused ||
|
||||
state == AppLifecycleState.inactive) {
|
||||
globalState.appController.savePreferences();
|
||||
} else {
|
||||
render?.resume();
|
||||
}
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
if (state == AppLifecycleState.inactive) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
detectionState.tryStartCheck();
|
||||
});
|
||||
if (system.isAndroid) {
|
||||
globalState.appController.tryStartCore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,16 +87,13 @@ class _CoreContainerState extends ConsumerState<CoreManager>
|
||||
ref
|
||||
.read(providersProvider.notifier)
|
||||
.setProvider(await coreController.getExternalProvider(providerName));
|
||||
debouncer.call(FunctionTag.loadedProvider, () async {
|
||||
globalState.appController.updateGroupsDebounce();
|
||||
}, duration: const Duration(milliseconds: 5000));
|
||||
globalState.appController.updateGroupsDebounce();
|
||||
super.onLoaded(providerName);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onCrash(String message) async {
|
||||
if (!globalState.isUserDisconnected &&
|
||||
WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) {
|
||||
if (!globalState.isUserDisconnected) {
|
||||
context.showNotifier(message);
|
||||
}
|
||||
globalState.isUserDisconnected = false;
|
||||
|
||||
@@ -2,8 +2,8 @@ export 'android_manager.dart';
|
||||
export 'app_manager.dart';
|
||||
export 'connectivity_manager.dart';
|
||||
export 'core_manager.dart';
|
||||
export 'message_manager.dart';
|
||||
export 'proxy_manager.dart';
|
||||
export 'status_manager.dart';
|
||||
export 'theme_manager.dart';
|
||||
export 'tile_manager.dart';
|
||||
export 'tray_manager.dart';
|
||||
|
||||
150
lib/manager/message_manager.dart
Normal file
@@ -0,0 +1,150 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/widgets/fade_box.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MessageManager extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const MessageManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
State<MessageManager> createState() => MessageManagerState();
|
||||
}
|
||||
|
||||
class MessageManagerState extends State<MessageManager> {
|
||||
final _messagesNotifier = ValueNotifier<List<CommonMessage>>([]);
|
||||
final _bufferMessages = Queue<CommonMessage>();
|
||||
final _activeTimers = <String, Timer>{};
|
||||
bool _isDisplayingMessage = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_messagesNotifier.dispose();
|
||||
for (final timer in _activeTimers.values) {
|
||||
timer.cancel();
|
||||
}
|
||||
_activeTimers.clear();
|
||||
_bufferMessages.clear();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void message(String text) {
|
||||
final commonMessage = CommonMessage(id: utils.uuidV4, text: text);
|
||||
_bufferMessages.add(commonMessage);
|
||||
commonPrint.log('message: $text');
|
||||
_processQueue();
|
||||
}
|
||||
|
||||
void _cancelMessage(String id) {
|
||||
_bufferMessages.removeWhere((msg) => msg.id == id);
|
||||
if (_activeTimers.containsKey(id)) {
|
||||
_removeMessage(id);
|
||||
}
|
||||
}
|
||||
|
||||
void _processQueue() {
|
||||
if (_isDisplayingMessage || _bufferMessages.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_isDisplayingMessage = true;
|
||||
final message = _bufferMessages.removeFirst();
|
||||
_messagesNotifier.value = List.from(_messagesNotifier.value)..add(message);
|
||||
final timer = Timer(message.duration, () {
|
||||
_removeMessage(message.id);
|
||||
});
|
||||
_activeTimers[message.id] = timer;
|
||||
}
|
||||
|
||||
void _removeMessage(String id) {
|
||||
_activeTimers.remove(id)?.cancel();
|
||||
final currentMessages = List<CommonMessage>.from(_messagesNotifier.value);
|
||||
currentMessages.removeWhere((msg) => msg.id == id);
|
||||
_messagesNotifier.value = currentMessages;
|
||||
_isDisplayingMessage = false;
|
||||
_processQueue();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
widget.child,
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _messagesNotifier,
|
||||
builder: (_, messages, _) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: kToolbarHeight + 12,
|
||||
left: 12,
|
||||
right: 12,
|
||||
),
|
||||
child: FadeThroughBox(
|
||||
alignment: Alignment.topRight,
|
||||
child: messages.isEmpty
|
||||
? SizedBox()
|
||||
: LayoutBuilder(
|
||||
key: Key(messages.last.id),
|
||||
builder: (_, constraints) {
|
||||
return Dismissible(
|
||||
key: ValueKey(messages.last.id),
|
||||
onDismissed: (_) {
|
||||
_cancelMessage(messages.last.id);
|
||||
},
|
||||
child: Card(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(14),
|
||||
),
|
||||
),
|
||||
elevation: 10,
|
||||
color: context.colorScheme.surfaceContainerHigh,
|
||||
child: Container(
|
||||
width: min(constraints.maxWidth, 500),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
messages.last.text,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
IconButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () {
|
||||
_cancelMessage(messages.last.id);
|
||||
},
|
||||
icon: Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||