From c6266b7917556c47ec02ed4f4896cce13e23469b Mon Sep 17 00:00:00 2001 From: chen08209 Date: Mon, 3 Feb 2025 23:32:00 +0800 Subject: [PATCH] Add windows storage corruption detection Fix core crash caused by windows resource manager restart Optimize logs, requests, access to pages Fix macos bypass domain issues --- .../kotlin/com/follow/clash/models/Package.kt | 2 +- .../com/follow/clash/plugins/AppPlugin.kt | 3 +- lib/clash/core.dart | 4 +- lib/clash/interface.dart | 7 +- lib/common/common.dart | 3 +- lib/common/context.dart | 19 + lib/common/function.dart | 46 +- lib/common/list.dart | 56 +- lib/common/lock.dart | 2 +- lib/common/measure.dart | 9 +- lib/common/navigation.dart | 10 +- lib/common/other.dart | 48 +- lib/common/path.dart | 17 +- lib/common/picker.dart | 16 +- lib/common/preferences.dart | 42 +- lib/common/render.dart | 1 + lib/common/scroll.dart | 90 ++ lib/common/view.dart | 20 + lib/controller.dart | 40 +- lib/fragments/access.dart | 83 +- lib/fragments/config/dns.dart | 4 +- lib/fragments/config/network.dart | 8 +- lib/fragments/connection/connections.dart | 155 +++ lib/fragments/connection/item.dart | 150 +++ lib/fragments/connection/requests.dart | 239 ++++ lib/fragments/connections.dart | 349 ----- lib/fragments/dashboard/dashboard.dart | 4 +- lib/fragments/fragments.dart | 4 +- lib/fragments/logs.dart | 482 +++---- lib/fragments/profiles/profiles.dart | 3 +- lib/fragments/proxies/common.dart | 2 +- lib/fragments/proxies/list.dart | 7 +- lib/fragments/proxies/providers.dart | 4 +- lib/fragments/proxies/proxies.dart | 6 +- lib/fragments/proxies/tab.dart | 4 +- lib/fragments/requests.dart | 317 ----- lib/fragments/resources.dart | 10 +- lib/fragments/tools.dart | 20 +- lib/l10n/arb/intl_en.arb | 3 +- lib/l10n/arb/intl_zh_CN.arb | 3 +- lib/l10n/intl/messages_all.dart | 13 +- lib/l10n/intl/messages_en.dart | 1076 ++++++++------- lib/l10n/intl/messages_zh_CN.dart | 773 +++++------ lib/l10n/l10n.dart | 1180 +++-------------- lib/main.dart | 4 +- lib/manager/app_state_manager.dart | 5 +- lib/manager/window_manager.dart | 1 - lib/models/app.dart | 53 +- lib/models/common.dart | 82 +- lib/models/generated/common.freezed.dart | 298 ++--- lib/models/generated/common.g.dart | 44 +- lib/models/generated/widget.freezed.dart | 185 +++ lib/models/selector.dart | 2 +- lib/models/widget.dart | 10 + lib/pages/home.dart | 125 +- lib/widgets/chip.dart | 6 +- lib/widgets/connection_item.dart | 167 --- lib/widgets/popup.dart | 4 - lib/widgets/scaffold.dart | 238 +++- lib/widgets/scroll.dart | 25 + lib/widgets/view.dart | 20 + lib/widgets/widgets.dart | 2 +- plugins/proxy/lib/proxy.dart | 2 +- pubspec.lock | 28 +- pubspec.yaml | 6 +- 65 files changed, 3070 insertions(+), 3571 deletions(-) create mode 100644 lib/common/view.dart create mode 100644 lib/fragments/connection/connections.dart create mode 100644 lib/fragments/connection/item.dart create mode 100644 lib/fragments/connection/requests.dart delete mode 100644 lib/fragments/connections.dart delete mode 100644 lib/fragments/requests.dart delete mode 100644 lib/widgets/connection_item.dart create mode 100644 lib/widgets/scroll.dart create mode 100644 lib/widgets/view.dart diff --git a/android/app/src/main/kotlin/com/follow/clash/models/Package.kt b/android/app/src/main/kotlin/com/follow/clash/models/Package.kt index dbacf0f2..967b5f3d 100644 --- a/android/app/src/main/kotlin/com/follow/clash/models/Package.kt +++ b/android/app/src/main/kotlin/com/follow/clash/models/Package.kt @@ -4,5 +4,5 @@ data class Package( val packageName: String, val label: String, val isSystem: Boolean, - val firstInstallTime: Long, + val lastUpdateTime: Long, ) diff --git a/android/app/src/main/kotlin/com/follow/clash/plugins/AppPlugin.kt b/android/app/src/main/kotlin/com/follow/clash/plugins/AppPlugin.kt index 640f854a..8683291f 100644 --- a/android/app/src/main/kotlin/com/follow/clash/plugins/AppPlugin.kt +++ b/android/app/src/main/kotlin/com/follow/clash/plugins/AppPlugin.kt @@ -37,7 +37,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import java.io.File import java.lang.ref.WeakReference @@ -302,7 +301,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware packageName = it.packageName, label = it.applicationInfo.loadLabel(packageManager).toString(), isSystem = (it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 1, - firstInstallTime = it.firstInstallTime + lastUpdateTime = it.lastUpdateTime ) }?.let { packages.addAll(it) } return packages diff --git a/lib/clash/core.dart b/lib/clash/core.dart index 8f8f1a7e..e92831d1 100644 --- a/lib/clash/core.dart +++ b/lib/clash/core.dart @@ -33,7 +33,7 @@ class ClashCore { } static Future initGeo() async { - final homePath = await appPath.getHomeDirPath(); + final homePath = await appPath.homeDirPath; final homeDir = Directory(homePath); final isExists = await homeDir.exists(); if (!isExists) { @@ -68,7 +68,7 @@ class ClashCore { required Config config, }) async { await initGeo(); - final homeDirPath = await appPath.getHomeDirPath(); + final homeDirPath = await appPath.homeDirPath; return await clashInterface.init(homeDirPath); } diff --git a/lib/clash/interface.dart b/lib/clash/interface.dart index 1ff58c41..4beead84 100644 --- a/lib/clash/interface.dart +++ b/lib/clash/interface.dart @@ -268,9 +268,9 @@ abstract class ClashHandlerInterface with ClashInterface { @override Future updateGeoData(UpdateGeoDataParams params) { return invoke( - method: ActionMethod.updateGeoData, - data: json.encode(params), - ); + method: ActionMethod.updateGeoData, + data: json.encode(params), + timeout: Duration(minutes: 1)); } @override @@ -292,6 +292,7 @@ abstract class ClashHandlerInterface with ClashInterface { return invoke( method: ActionMethod.updateExternalProvider, data: providerName, + timeout: Duration(minutes: 1), ); } diff --git a/lib/common/common.dart b/lib/common/common.dart index 54fa057b..7a4d1b7a 100644 --- a/lib/common/common.dart +++ b/lib/common/common.dart @@ -34,4 +34,5 @@ export 'text.dart'; export 'tray.dart'; export 'window.dart'; export 'windows.dart'; -export 'render.dart'; \ No newline at end of file +export 'render.dart'; +export 'view.dart'; \ No newline at end of file diff --git a/lib/common/context.dart b/lib/common/context.dart index 692b1304..47e364db 100644 --- a/lib/common/context.dart +++ b/lib/common/context.dart @@ -22,4 +22,23 @@ extension BuildContextExtension on BuildContext { ColorScheme get colorScheme => Theme.of(this).colorScheme; TextTheme get textTheme => Theme.of(this).textTheme; + + T? findLastStateOfType() { + T? state; + + visitor(Element element) { + if(!element.mounted){ + return; + } + if(element is StatefulElement){ + if (element.state is T) { + state = element.state as T; + } + } + element.visitChildren(visitor); + } + + visitor(this as Element); + return state; + } } diff --git a/lib/common/function.dart b/lib/common/function.dart index 1b9c077f..c46a2250 100644 --- a/lib/common/function.dart +++ b/lib/common/function.dart @@ -1,7 +1,7 @@ import 'dart:async'; class Debouncer { - Map operators = {}; + final Map _operations = {}; call( dynamic tag, @@ -9,14 +9,15 @@ class Debouncer { List? args, Duration duration = const Duration(milliseconds: 600), }) { - final timer = operators[tag]; + final timer = _operations[tag]; if (timer != null) { timer.cancel(); } - operators[tag] = Timer( + _operations[tag] = Timer( duration, () { - operators.remove(tag); + _operations[tag]?.cancel(); + _operations.remove(tag); Function.apply( func, args, @@ -26,8 +27,43 @@ class Debouncer { } cancel(dynamic tag) { - operators[tag]?.cancel(); + _operations[tag]?.cancel(); } } +class Throttler { + final Map _operations = {}; + + call( + String tag, + Function func, { + List? args, + Duration duration = const Duration(milliseconds: 600), + }) { + final timer = _operations[tag]; + if (timer != null) { + return true; + } + _operations[tag] = Timer( + duration, + () { + _operations[tag]?.cancel(); + _operations.remove(tag); + Function.apply( + func, + args, + ); + }, + ); + return false; + } + + cancel(dynamic tag) { + _operations[tag]?.cancel(); + } +} + + final debouncer = Debouncer(); + +final throttler = Throttler(); \ No newline at end of file diff --git a/lib/common/list.dart b/lib/common/list.dart index 94bae2fc..6107f6aa 100644 --- a/lib/common/list.dart +++ b/lib/common/list.dart @@ -1,3 +1,55 @@ +import 'dart:collection'; + +class FixedList { + final int maxLength; + final List _list = []; + + FixedList(this.maxLength); + + add(T item) { + if (_list.length == maxLength) { + _list.removeAt(0); + } + _list.add(item); + } + + List get list => List.unmodifiable(_list); + + int get length => _list.length; + + T operator [](int index) => _list[index]; +} + +class FixedMap { + final int maxSize; + final Map _map = {}; + final Queue _queue = Queue(); + + FixedMap(this.maxSize); + + put(K key, V value) { + if (_map.length == maxSize) { + final oldestKey = _queue.removeFirst(); + _map.remove(oldestKey); + } + _map[key] = value; + _queue.add(key); + } + + clear(){ + _map.clear(); + _queue.clear(); + } + + V? get(K key) => _map[key]; + + bool containsKey(K key) => _map.containsKey(key); + + int get length => _map.length; + + Map get map => Map.unmodifiable(_map); +} + extension ListExtension on List { List intersection(List list) { return where((item) => list.contains(item)).toList(); @@ -17,8 +69,8 @@ extension ListExtension on List { } List safeSublist(int start) { - if(start <= 0) return this; - if(start > length) return []; + if (start <= 0) return this; + if (start > length) return []; return sublist(start); } } diff --git a/lib/common/lock.dart b/lib/common/lock.dart index 42467e78..f9615bf6 100644 --- a/lib/common/lock.dart +++ b/lib/common/lock.dart @@ -15,7 +15,7 @@ class SingleInstanceLock { Future acquire() async { try { - final lockFilePath = await appPath.getLockFilePath(); + final lockFilePath = await appPath.lockFilePath; final lockFile = File(lockFilePath); await lockFile.create(); _accessFile = await lockFile.open(mode: FileMode.write); diff --git a/lib/common/measure.dart b/lib/common/measure.dart index 7dae911c..cd148a24 100644 --- a/lib/common/measure.dart +++ b/lib/common/measure.dart @@ -11,13 +11,18 @@ class Measure { WidgetsBinding.instance.platformDispatcher.textScaleFactor, ); - Size computeTextSize(Text text) { + Size computeTextSize( + Text text, { + double maxWidth = double.infinity, + }) { final textPainter = TextPainter( text: TextSpan(text: text.data, style: text.style), maxLines: text.maxLines, textScaler: _textScale, textDirection: text.textDirection ?? TextDirection.ltr, - )..layout(); + )..layout( + maxWidth: maxWidth, + ); return textPainter.size; } diff --git a/lib/common/navigation.dart b/lib/common/navigation.dart index 98c33f66..02184167 100644 --- a/lib/common/navigation.dart +++ b/lib/common/navigation.dart @@ -30,16 +30,16 @@ class Navigation { fragment: ProfilesFragment(), ), const NavigationItem( - icon: Icon(Icons.view_timeline), + icon: Icon(Icons.view_timeline), label: "requests", - fragment: RequestsFragment(), + fragment: RequestsFragment(), description: "requestsDesc", modes: [NavigationItemMode.desktop, NavigationItemMode.more], ), const NavigationItem( - icon: Icon(Icons.ballot), + icon: Icon(Icons.ballot), label: "connections", - fragment: ConnectionsFragment(), + fragment: ConnectionsFragment(), description: "connectionsDesc", modes: [NavigationItemMode.desktop, NavigationItemMode.more], ), @@ -49,7 +49,7 @@ class Navigation { description: "resourcesDesc", keep: false, fragment: Resources(), - modes: [NavigationItemMode.desktop, NavigationItemMode.more], + modes: [NavigationItemMode.more], ), NavigationItem( icon: const Icon(Icons.adb), diff --git a/lib/common/other.dart b/lib/common/other.dart index 6ce58f4f..4be06537 100644 --- a/lib/common/other.dart +++ b/lib/common/other.dart @@ -1,15 +1,11 @@ import 'dart:io'; -import 'dart:isolate'; import 'dart:math'; -import 'dart:typed_data'; import 'dart:ui'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:flutter/material.dart'; -import 'package:image/image.dart' as img; import 'package:lpinyin/lpinyin.dart'; -import 'package:zxing2/qrcode.dart'; class Other { Color? getDelayColor(int? delay) { @@ -34,6 +30,26 @@ class Other { ); } + String generateRandomString({int minLength = 10, int maxLength = 100}) { + const latinChars = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + final random = Random(); + + int length = minLength + random.nextInt(maxLength - minLength + 1); + + String result = ''; + for (int i = 0; i < length; i++) { + if (random.nextBool()) { + result += + String.fromCharCode(0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1)); + } else { + result += latinChars[random.nextInt(latinChars.length)]; + } + } + + return result; + } + String get uuidV4 { final Random random = Random(); final bytes = List.generate(16, (_) => random.nextInt(256)); @@ -165,30 +181,6 @@ class Other { : ""; } - Future parseQRCode(Uint8List? bytes) { - return Isolate.run(() { - if (bytes == null) return null; - img.Image? image = img.decodeImage(bytes); - LuminanceSource source = RGBLuminanceSource( - image!.width, - image.height, - image - .convert(numChannels: 4) - .getBytes(order: img.ChannelOrder.abgr) - .buffer - .asInt32List(), - ); - final bitmap = BinaryBitmap(GlobalHistogramBinarizer(source)); - final reader = QRCodeReader(); - try { - final result = reader.decode(bitmap); - return result.text; - } catch (_) { - return null; - } - }); - } - String? getFileNameForDisposition(String? disposition) { if (disposition == null) return null; final parseValue = HeaderValue.parse(disposition); diff --git a/lib/common/path.dart b/lib/common/path.dart index 7d6a1488..77093313 100644 --- a/lib/common/path.dart +++ b/lib/common/path.dart @@ -48,35 +48,40 @@ class AppPath { return join(executableDirPath, "$appHelperService$executableExtension"); } - Future getDownloadDirPath() async { + Future get downloadDirPath async { final directory = await downloadDir.future; return directory.path; } - Future getHomeDirPath() async { + Future get homeDirPath async { final directory = await dataDir.future; return directory.path; } - Future getLockFilePath() async { + Future get lockFilePath async { final directory = await dataDir.future; return join(directory.path, "FlClash.lock"); } - Future getProfilesPath() async { + Future get sharedPreferencesPath async { + final directory = await dataDir.future; + return join(directory.path, "shared_preferences.json"); + } + + Future get profilesPath async { final directory = await dataDir.future; return join(directory.path, profilesDirectoryName); } Future getProfilePath(String? id) async { if (id == null) return null; - final directory = await getProfilesPath(); + final directory = await profilesPath; return join(directory, "$id.yaml"); } Future getProvidersPath(String? id) async { if (id == null) return null; - final directory = await getProfilesPath(); + final directory = await profilesPath; return join(directory, "providers", id); } diff --git a/lib/common/picker.dart b/lib/common/picker.dart index c894bfd6..fb721907 100644 --- a/lib/common/picker.dart +++ b/lib/common/picker.dart @@ -4,13 +4,14 @@ import 'dart:typed_data'; import 'package:file_picker/file_picker.dart'; import 'package:fl_clash/common/common.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; class Picker { Future pickerFile() async { final filePickerResult = await FilePicker.platform.pickFiles( withData: true, allowMultiple: false, - initialDirectory: await appPath.getDownloadDirPath(), + initialDirectory: await appPath.downloadDirPath, ); return filePickerResult?.files.first; } @@ -18,7 +19,7 @@ class Picker { Future saveFile(String fileName, Uint8List bytes) async { final path = await FilePicker.platform.saveFile( fileName: fileName, - initialDirectory: await appPath.getDownloadDirPath(), + initialDirectory: await appPath.downloadDirPath, bytes: Platform.isAndroid ? bytes : null, ); if (!Platform.isAndroid && path != null) { @@ -30,9 +31,14 @@ class Picker { Future pickerConfigQRCode() async { final xFile = await ImagePicker().pickImage(source: ImageSource.gallery); - final bytes = await xFile?.readAsBytes(); - if (bytes == null) return null; - final result = await other.parseQRCode(bytes); + if (xFile == null) { + return null; + } + final controller = MobileScannerController(); + final capture = await controller.analyzeImage(xFile.path, formats: [ + BarcodeFormat.qrCode, + ]); + final result = capture?.barcodes.first.rawValue; if (result == null || !result.isUrl) { throw appLocalizations.pleaseUploadValidQrcode; } diff --git a/lib/common/preferences.dart b/lib/common/preferences.dart index 678cc044..18064f99 100644 --- a/lib/common/preferences.dart +++ b/lib/common/preferences.dart @@ -1,19 +1,21 @@ import 'dart:async'; import 'dart:convert'; -import 'package:flutter/cupertino.dart'; +import 'package:fl_clash/models/models.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../models/models.dart'; + import 'constant.dart'; class Preferences { static Preferences? _instance; - Completer sharedPreferencesCompleter = Completer(); + Completer sharedPreferencesCompleter = Completer(); + + Future get isInit async => await sharedPreferencesCompleter.future != null; Preferences._internal() { - SharedPreferences.getInstance() - .then((value) => sharedPreferencesCompleter.complete(value)); + SharedPreferences.getInstance().then((value) => sharedPreferencesCompleter.complete(value)) + .onError((_,__)=>sharedPreferencesCompleter.complete(null)); } factory Preferences() { @@ -21,52 +23,44 @@ class Preferences { return _instance!; } + Future getClashConfig() async { final preferences = await sharedPreferencesCompleter.future; - final clashConfigString = preferences.getString(clashConfigKey); + final clashConfigString = preferences?.getString(clashConfigKey); if (clashConfigString == null) return null; final clashConfigMap = json.decode(clashConfigString); - try { - return ClashConfig.fromJson(clashConfigMap); - } catch (e) { - debugPrint(e.toString()); - return null; - } + return ClashConfig.fromJson(clashConfigMap); } Future saveClashConfig(ClashConfig clashConfig) async { final preferences = await sharedPreferencesCompleter.future; - return preferences.setString( + preferences?.setString( clashConfigKey, json.encode(clashConfig), ); + return true; } Future getConfig() async { final preferences = await sharedPreferencesCompleter.future; - final configString = preferences.getString(configKey); + final configString = preferences?.getString(configKey); if (configString == null) return null; final configMap = json.decode(configString); - try { - return Config.fromJson(configMap); - } catch (e) { - debugPrint(e.toString()); - return null; - } + return Config.fromJson(configMap); } Future saveConfig(Config config) async { final preferences = await sharedPreferencesCompleter.future; - return preferences.setString( + return await preferences?.setString( configKey, json.encode(config), - ); + ) ?? false; } clearPreferences() async { final sharedPreferencesIns = await sharedPreferencesCompleter.future; - sharedPreferencesIns.clear(); + sharedPreferencesIns?.clear(); } } -final preferences = Preferences(); \ No newline at end of file +final preferences = Preferences(); diff --git a/lib/common/render.dart b/lib/common/render.dart index d427e916..36898713 100644 --- a/lib/common/render.dart +++ b/lib/common/render.dart @@ -49,6 +49,7 @@ class Render { _isPaused = false; _dispatcher.onBeginFrame = _beginFrame; _dispatcher.onDrawFrame = _drawFrame; + _dispatcher.scheduleFrame(); debugPrint("[App] resume"); } } diff --git a/lib/common/scroll.dart b/lib/common/scroll.dart index cb48baff..d8266289 100644 --- a/lib/common/scroll.dart +++ b/lib/common/scroll.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import 'dart:ui'; import 'package:fl_clash/common/common.dart'; @@ -15,6 +16,8 @@ class BaseScrollBehavior extends MaterialScrollBehavior { }; } +class BaseScrollBehavior2 extends ScrollBehavior {} + class HiddenBarScrollBehavior extends BaseScrollBehavior { @override Widget buildScrollbar( @@ -40,3 +43,90 @@ class ShowBarScrollBehavior extends BaseScrollBehavior { ); } } + +class NextClampingScrollPhysics extends ClampingScrollPhysics { + @override + Simulation? createBallisticSimulation( + ScrollMetrics position, double velocity) { + final Tolerance tolerance = toleranceFor(position); + if (position.outOfRange) { + double? end; + if (position.pixels > position.maxScrollExtent) { + end = position.maxScrollExtent; + } + if (position.pixels < position.minScrollExtent) { + end = position.minScrollExtent; + } + assert(end != null); + return ScrollSpringSimulation( + spring, + end!, + end, + min(0.0, velocity), + tolerance: tolerance, + ); + } + if (velocity.abs() < tolerance.velocity) { + return null; + } + if (velocity > 0.0 && position.pixels >= position.maxScrollExtent) { + return null; + } + if (velocity < 0.0 && position.pixels <= position.minScrollExtent) { + return null; + } + return ClampingScrollSimulation( + position: position.pixels, + velocity: velocity, + tolerance: tolerance, + ); + } +} + +class ReverseScrollController extends ScrollController { + ReverseScrollController({ + super.initialScrollOffset, + super.keepScrollOffset, + super.debugLabel, + }); + + @override + ScrollPosition createScrollPosition( + ScrollPhysics physics, + ScrollContext context, + ScrollPosition? oldPosition, + ) { + return ReverseScrollPosition( + physics: physics, + context: context, + initialPixels: initialScrollOffset, + keepScrollOffset: keepScrollOffset, + oldPosition: oldPosition, + debugLabel: debugLabel, + ); + } +} + +class ReverseScrollPosition extends ScrollPositionWithSingleContext { + ReverseScrollPosition({ + required super.physics, + required super.context, + super.initialPixels = 0.0, + super.keepScrollOffset, + super.oldPosition, + super.debugLabel, + }) : _initialPixels = initialPixels ?? 0; + + final double _initialPixels; + + bool _isInit = false; + + @override + bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) { + if (!_isInit) { + correctPixels(maxScrollExtent); + _isInit = true; + } + return super.applyContentDimensions(minScrollExtent, maxScrollExtent); + } +} diff --git a/lib/common/view.dart b/lib/common/view.dart new file mode 100644 index 00000000..a118b37d --- /dev/null +++ b/lib/common/view.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'context.dart'; + +mixin ViewMixin on State { + List get actions => []; + + Widget? get floatingActionButton => null; + + initViewState() { + final commonScaffoldState = context.commonScaffoldState; + commonScaffoldState?.actions = actions; + commonScaffoldState?.floatingActionButton = floatingActionButton; + commonScaffoldState?.onSearch = onSearch; + commonScaffoldState?.onKeywordsUpdate = onKeywordsUpdate; + } + + Function(String)? get onSearch => null; + + Function(List)? get onKeywordsUpdate => null; +} diff --git a/lib/controller.dart b/lib/controller.dart index 32c2d9c1..9ace2345 100644 --- a/lib/controller.dart +++ b/lib/controller.dart @@ -338,11 +338,27 @@ class AppController { } } - init() async { - final isDisclaimerAccepted = await handlerDisclaimer(); - if (!isDisclaimerAccepted) { - handleExit(); + _handlePreference() async { + if (await preferences.isInit) { + return; } + final res = await globalState.showMessage( + title: appLocalizations.tip, + message: TextSpan(text: appLocalizations.cacheCorrupt), + ); + if (res) { + final file = File(await appPath.sharedPreferencesPath); + final isExists = await file.exists(); + if (isExists) { + await file.delete(); + } + } + await handleExit(); + } + + init() async { + await _handlePreference(); + await _handlerDisclaimer(); await globalState.initCore( appState: appState, clashConfig: clashConfig, @@ -473,11 +489,15 @@ class AppController { false; } - Future handlerDisclaimer() async { + _handlerDisclaimer() async { if (config.appSetting.disclaimerAccepted) { - return true; + return; + } + final isDisclaimerAccepted = await showDisclaimer(); + if (!isDisclaimerAccepted) { + await handleExit(); } - return showDisclaimer(); + return; } addProfileFormURL(String url) async { @@ -673,8 +693,8 @@ class AppController { } Future> backupData() async { - final homeDirPath = await appPath.getHomeDirPath(); - final profilesPath = await appPath.getProfilesPath(); + final homeDirPath = await appPath.homeDirPath; + final profilesPath = await appPath.profilesPath; final configJson = config.toJson(); final clashConfigJson = clashConfig.toJson(); return Isolate.run>(() async { @@ -705,7 +725,7 @@ class AppController { final zipDecoder = ZipDecoder(); return zipDecoder.decodeBytes(data); }); - final homeDirPath = await appPath.getHomeDirPath(); + final homeDirPath = await appPath.homeDirPath; final configs = archive.files.where((item) => item.name.endsWith(".json")).toList(); final profiles = diff --git a/lib/fragments/access.dart b/lib/fragments/access.dart index 2d134c11..46211014 100644 --- a/lib/fragments/access.dart +++ b/lib/fragments/access.dart @@ -20,11 +20,13 @@ class AccessFragment extends StatefulWidget { class _AccessFragmentState extends State { List acceptList = []; List rejectList = []; + late ScrollController _controller; @override void initState() { super.initState(); _updateInitList(); + _controller = ScrollController(); WidgetsBinding.instance.addPostFrameCallback((_) { final appState = globalState.appController.appState; if (appState.packages.isEmpty) { @@ -35,6 +37,12 @@ class _AccessFragmentState extends State { }); } + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + _updateInitList() { final accessControl = globalState.appController.config.accessControl; acceptList = accessControl.acceptList; @@ -52,8 +60,8 @@ class _AccessFragmentState extends State { rejectList: rejectList, ), ).then((_) => setState(() { - _updateInitList(); - })); + _updateInitList(); + })); }, icon: const Icon(Icons.search), ); @@ -268,39 +276,44 @@ class _AccessFragmentState extends State { ? const Center( child: CircularProgressIndicator(), ) - : ListView.builder( - itemCount: packages.length, - itemBuilder: (_, index) { - final package = packages[index]; - return PackageListItem( - key: Key(package.packageName), - package: package, - value: - valueList.contains(package.packageName), - isActive: isAccessControl, - onChanged: (value) { - if (value == true) { - valueList.add(package.packageName); - } else { - valueList.remove(package.packageName); - } - final config = - globalState.appController.config; - if (accessControlMode == - AccessControlMode.acceptSelected) { - config.accessControl = - config.accessControl.copyWith( - acceptList: valueList, - ); - } else { - config.accessControl = - config.accessControl.copyWith( - rejectList: valueList, - ); - } - }, - ); - }, + : CommonScrollBar( + controller: _controller, + child: ListView.builder( + controller: _controller, + itemCount: packages.length, + itemExtent: 72, + itemBuilder: (_, index) { + final package = packages[index]; + return PackageListItem( + key: Key(package.packageName), + package: package, + value: + valueList.contains(package.packageName), + isActive: isAccessControl, + onChanged: (value) { + if (value == true) { + valueList.add(package.packageName); + } else { + valueList.remove(package.packageName); + } + final config = + globalState.appController.config; + if (accessControlMode == + AccessControlMode.acceptSelected) { + config.accessControl = + config.accessControl.copyWith( + acceptList: valueList, + ); + } else { + config.accessControl = + config.accessControl.copyWith( + rejectList: valueList, + ); + } + }, + ); + }, + ), ), ), ], diff --git a/lib/fragments/config/dns.dart b/lib/fragments/config/dns.dart index 7f11af68..97f6c982 100644 --- a/lib/fragments/config/dns.dart +++ b/lib/fragments/config/dns.dart @@ -705,9 +705,7 @@ class DnsListView extends StatelessWidget { _initActions(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.actions = [ + context.commonScaffoldState?.actions = [ IconButton( onPressed: () async { final res = await globalState.showMessage( diff --git a/lib/fragments/config/network.dart b/lib/fragments/config/network.dart index 1674e389..f3fb6a0a 100644 --- a/lib/fragments/config/network.dart +++ b/lib/fragments/config/network.dart @@ -206,9 +206,7 @@ class BypassDomainItem extends StatelessWidget { _initActions(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.actions = [ + context.commonScaffoldState?.actions = [ IconButton( onPressed: () async { final res = await globalState.showMessage( @@ -378,9 +376,7 @@ class NetworkListView extends StatelessWidget { _initActions(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.actions = [ + context.commonScaffoldState?.actions = [ IconButton( onPressed: () async { final res = await globalState.showMessage( diff --git a/lib/fragments/connection/connections.dart b/lib/fragments/connection/connections.dart new file mode 100644 index 00000000..ed0e63c0 --- /dev/null +++ b/lib/fragments/connection/connections.dart @@ -0,0 +1,155 @@ +import 'dart:async'; + +import 'package:fl_clash/clash/clash.dart'; +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; +import 'package:fl_clash/widgets/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'item.dart'; + +class ConnectionsFragment extends StatefulWidget { + const ConnectionsFragment({super.key}); + + @override + State createState() => _ConnectionsFragmentState(); +} + +class _ConnectionsFragmentState extends State + with ViewMixin { + final _connectionsStateNotifier = ValueNotifier( + const ConnectionsState(), + ); + final ScrollController _scrollController = ScrollController( + keepScrollOffset: false, + ); + + Timer? timer; + + @override + List get actions => [ + IconButton( + onPressed: () async { + clashCore.closeConnections(); + _connectionsStateNotifier.value = + _connectionsStateNotifier.value.copyWith( + connections: await clashCore.getConnections(), + ); + }, + icon: const Icon(Icons.delete_sweep_outlined), + ), + ]; + + @override + get onSearch => (value) { + _connectionsStateNotifier.value = + _connectionsStateNotifier.value.copyWith( + query: value, + ); + }; + + @override + get onKeywordsUpdate => (keywords) { + _connectionsStateNotifier.value = + _connectionsStateNotifier.value.copyWith(keywords: keywords); + }; + + _updateConnections() async { + WidgetsBinding.instance.addPostFrameCallback((_) async { + _connectionsStateNotifier.value = + _connectionsStateNotifier.value.copyWith( + connections: await clashCore.getConnections(), + ); + timer = Timer(Duration(seconds: 1), () async { + _updateConnections(); + }); + }); + } + + @override + void initState() { + super.initState(); + _updateConnections(); + } + + _initActions() { + WidgetsBinding.instance.addPostFrameCallback( + (_) { + initViewState(); + }, + ); + } + + _handleBlockConnection(String id) async { + clashCore.closeConnection(id); + _connectionsStateNotifier.value = _connectionsStateNotifier.value.copyWith( + connections: await clashCore.getConnections(), + ); + } + + @override + void dispose() { + timer?.cancel(); + _connectionsStateNotifier.dispose(); + _scrollController.dispose(); + timer = null; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, appState) => + appState.currentLabel == 'connections' || + appState.viewMode == ViewMode.mobile && + appState.currentLabel == "tools", + builder: (_, isCurrent, child) { + if (isCurrent == null || isCurrent) { + _initActions(); + } + return child!; + }, + child: ValueListenableBuilder( + valueListenable: _connectionsStateNotifier, + builder: (_, state, __) { + final connections = state.list; + if (connections.isEmpty) { + return NullStatus( + label: appLocalizations.nullConnectionsDesc, + ); + } + return CommonScrollBar( + controller: _scrollController, + child: ListView.separated( + controller: _scrollController, + itemBuilder: (_, index) { + final connection = connections[index]; + return ConnectionItem( + key: Key(connection.id), + connection: connection, + onClick: (value) { + context.commonScaffoldState?.addKeyword(value); + }, + trailing: IconButton( + icon: const Icon(Icons.block), + onPressed: () { + _handleBlockConnection(connection.id); + }, + ), + ); + }, + separatorBuilder: (BuildContext context, int index) { + return const Divider( + height: 0, + ); + }, + itemCount: connections.length, + ), + ); + }, + ), + ); + } +} diff --git a/lib/fragments/connection/item.dart b/lib/fragments/connection/item.dart new file mode 100644 index 00000000..fae04147 --- /dev/null +++ b/lib/fragments/connection/item.dart @@ -0,0 +1,150 @@ +import 'dart:io'; + +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; +import 'package:fl_clash/plugins/app.dart'; +import 'package:fl_clash/widgets/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class FindProcessBuilder extends StatelessWidget { + final Widget Function(bool value) builder; + + const FindProcessBuilder({ + super.key, + required this.builder, + }); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, clashConfig) => + clashConfig.findProcessMode == FindProcessMode.always && + Platform.isAndroid, + builder: (_, value, __) { + return builder(value); + }, + ); + } +} + +class ConnectionItem extends StatelessWidget { + final Connection connection; + final Function(String)? onClick; + final Widget? trailing; + + const ConnectionItem({ + super.key, + required this.connection, + this.onClick, + this.trailing, + }); + + Future _getPackageIcon(Connection connection) async { + return await app?.getPackageIcon(connection.metadata.process); + } + + String _getSourceText(Connection connection) { + final metadata = connection.metadata; + if (metadata.process.isEmpty) { + return connection.start.lastUpdateTimeDesc; + } + return "${metadata.process} · ${connection.start.lastUpdateTimeDesc}"; + } + + @override + Widget build(BuildContext context) { + final title = Text( + connection.desc, + style: context.textTheme.bodyLarge, + ); + final subTitle = Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 8, + ), + Text( + _getSourceText(connection), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox( + height: 8, + ), + Wrap( + runSpacing: 6, + spacing: 6, + children: [ + for (final chain in connection.chains) + CommonChip( + label: chain, + onPressed: () { + if (onClick == null) return; + onClick!(chain); + }, + ), + ], + ), + ], + ); + if (!Platform.isAndroid) { + return ListItem( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + tileTitleAlignment: ListTileTitleAlignment.titleHeight, + title: title, + subtitle: subTitle, + trailing: trailing, + ); + } + return FindProcessBuilder( + builder: (bool value) { + final leading = value + ? GestureDetector( + onTap: () { + if (onClick == null) return; + final process = connection.metadata.process; + if (process.isEmpty) return; + onClick!(process); + }, + child: Container( + margin: const EdgeInsets.only(top: 4), + width: 48, + height: 48, + child: FutureBuilder( + future: _getPackageIcon(connection), + builder: (_, snapshot) { + if (!snapshot.hasData && snapshot.data == null) { + return Container(); + } else { + return Image( + image: snapshot.data!, + gaplessPlayback: true, + width: 48, + height: 48, + ); + } + }, + ), + ), + ) + : null; + return ListItem( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + tileTitleAlignment: ListTileTitleAlignment.titleHeight, + leading: leading, + title: title, + subtitle: subTitle, + trailing: trailing, + ); + }, + ); + } +} diff --git a/lib/fragments/connection/requests.dart b/lib/fragments/connection/requests.dart new file mode 100644 index 00000000..8a6bffd2 --- /dev/null +++ b/lib/fragments/connection/requests.dart @@ -0,0 +1,239 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:math'; + +import 'package:collection/collection.dart'; +import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; +import 'package:fl_clash/state.dart'; +import 'package:fl_clash/widgets/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'item.dart'; + +double _preOffset = 0; + +class RequestsFragment extends StatefulWidget { + const RequestsFragment({super.key}); + + @override + State createState() => _RequestsFragmentState(); +} + +class _RequestsFragmentState extends State with ViewMixin { + final _requestsStateNotifier = + ValueNotifier(const ConnectionsState()); + List _requests = []; + + final ScrollController _scrollController = ScrollController( + initialScrollOffset: _preOffset != 0 ? _preOffset : double.maxFinite, + ); + + final FixedMap _cacheDynamicHeightMap = FixedMap(1000); + + double _currentMaxWidth = 0; + + @override + get onSearch => (value) { + _requestsStateNotifier.value = _requestsStateNotifier.value.copyWith( + query: value, + ); + }; + + @override + get onKeywordsUpdate => (keywords) { + _requestsStateNotifier.value = + _requestsStateNotifier.value.copyWith(keywords: keywords); + }; + + @override + void initState() { + super.initState(); + final appController = globalState.appController; + final appState = appController.appState; + _requestsStateNotifier.value = _requestsStateNotifier.value.copyWith( + connections: appState.requests, + ); + } + + _initActions() { + WidgetsBinding.instance.addPostFrameCallback( + (_) { + initViewState(); + }, + ); + } + + double _calcCacheHeight(Connection item) { + final cacheHeight = _cacheDynamicHeightMap.get(item.id); + if (cacheHeight != null) { + return cacheHeight; + } + final size = globalState.measure.computeTextSize( + Text( + item.desc, + style: context.textTheme.bodyLarge, + ), + maxWidth: _currentMaxWidth, + ); + final chainsText = item.chains.join(""); + final length = item.chains.length; + final chainSize = globalState.measure.computeTextSize( + Text( + chainsText, + style: context.textTheme.bodyMedium, + ), + maxWidth: (_currentMaxWidth - (length - 1) * 6 - length * 24), + ); + final baseHeight = globalState.measure.bodyMediumHeight; + final lines = (chainSize.height / baseHeight).round(); + final computerHeight = + size.height + chainSize.height + 24 + 24 * (lines - 1); + _cacheDynamicHeightMap.put(item.id, computerHeight); + return computerHeight; + } + + _handleTryClearCache(double maxWidth) { + if (_currentMaxWidth != maxWidth) { + _currentMaxWidth = maxWidth; + _cacheDynamicHeightMap.clear(); + } + } + + @override + void dispose() { + _requestsStateNotifier.dispose(); + _scrollController.dispose(); + _currentMaxWidth = 0; + _cacheDynamicHeightMap.clear(); + super.dispose(); + } + + Widget _wrapPage(Widget child) { + return Selector( + selector: (_, appState) => + appState.currentLabel == 'requests' || + appState.viewMode == ViewMode.mobile && + appState.currentLabel == "tools", + builder: (_, isCurrent, child) { + if (isCurrent == null || isCurrent) { + _initActions(); + } + return child!; + }, + child: child, + ); + } + + updateRequestsThrottler() { + throttler.call("request", () { + final isEquality = connectionListEquality.equals( + _requests, + _requestsStateNotifier.value.connections, + ); + if (isEquality) { + return; + } + WidgetsBinding.instance.addPostFrameCallback((_) { + _requestsStateNotifier.value = _requestsStateNotifier.value.copyWith( + connections: _requests, + ); + }); + }, duration: commonDuration); + } + + Widget _wrapRequestsUpdate(Widget child) { + return Selector>( + selector: (_, appState) => appState.requests, + shouldRebuild: (prev, next) { + final isEquality = connectionListEquality.equals(prev, next); + if (!isEquality) { + _requests = next; + updateRequestsThrottler(); + } + return !isEquality; + }, + builder: (_, next, child) { + return child!; + }, + child: child, + ); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (_, constraints) { + return FindProcessBuilder(builder: (value) { + _handleTryClearCache(constraints.maxWidth - 40 - (value ? 60 : 0)); + return _wrapPage( + _wrapRequestsUpdate( + ValueListenableBuilder( + valueListenable: _requestsStateNotifier, + builder: (_, state, __) { + final connections = state.list; + if (connections.isEmpty) { + return NullStatus( + label: appLocalizations.nullRequestsDesc, + ); + } + final items = connections + .map( + (connection) => ConnectionItem( + key: Key(connection.id), + connection: connection, + onClick: (value) { + context.commonScaffoldState?.addKeyword(value); + }, + ), + ) + .separated( + const Divider( + height: 0, + ), + ) + .toList(); + return Align( + alignment: Alignment.topCenter, + child: NotificationListener( + onNotification: (details) { + _preOffset = details.metrics.pixels; + return false; + }, + child: CommonScrollBar( + controller: _scrollController, + child: ListView.builder( + reverse: true, + shrinkWrap: true, + physics: NextClampingScrollPhysics(), + controller: _scrollController, + itemExtentBuilder: (index, __) { + final widget = items[index]; + if (widget.runtimeType == Divider) { + return 0; + } + final measure = globalState.measure; + final bodyMediumHeight = measure.bodyMediumHeight; + final connection = connections[(index / 2).floor()]; + final height = _calcCacheHeight(connection); + return height + bodyMediumHeight + 32; + }, + itemBuilder: (_, index) { + return items[index]; + }, + itemCount: items.length, + ), + ), + ), + ); + }, + ), + ), + ); + }); + }, + ); + } +} diff --git a/lib/fragments/connections.dart b/lib/fragments/connections.dart deleted file mode 100644 index ffcd0e74..00000000 --- a/lib/fragments/connections.dart +++ /dev/null @@ -1,349 +0,0 @@ -import 'dart:async'; - -import 'package:fl_clash/clash/clash.dart'; -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/models/models.dart'; -import 'package:fl_clash/widgets/widgets.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class ConnectionsFragment extends StatefulWidget { - const ConnectionsFragment({super.key}); - - @override - State createState() => _ConnectionsFragmentState(); -} - -class _ConnectionsFragmentState extends State { - final connectionsNotifier = - ValueNotifier(const ConnectionsAndKeywords()); - final ScrollController _scrollController = ScrollController( - keepScrollOffset: false, - ); - - Timer? timer; - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) async { - connectionsNotifier.value = connectionsNotifier.value.copyWith( - connections: await clashCore.getConnections(), - ); - if (timer != null) { - timer?.cancel(); - timer = null; - } - timer = Timer.periodic( - const Duration(seconds: 1), - (timer) async { - if (!context.mounted) { - return; - } - connectionsNotifier.value = connectionsNotifier.value.copyWith( - connections: await clashCore.getConnections(), - ); - }, - ); - }); - } - - _initActions() { - WidgetsBinding.instance.addPostFrameCallback( - (_) { - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.actions = [ - IconButton( - onPressed: () { - showSearch( - context: context, - delegate: ConnectionsSearchDelegate( - state: connectionsNotifier.value, - ), - ); - }, - icon: const Icon(Icons.search), - ), - IconButton( - onPressed: () async { - clashCore.closeConnections(); - connectionsNotifier.value = connectionsNotifier.value.copyWith( - connections: await clashCore.getConnections(), - ); - }, - icon: const Icon(Icons.delete_sweep_outlined), - ), - ]; - }, - ); - } - - _addKeyword(String keyword) { - final isContains = connectionsNotifier.value.keywords.contains(keyword); - if (isContains) return; - final keywords = List.from(connectionsNotifier.value.keywords) - ..add(keyword); - connectionsNotifier.value = connectionsNotifier.value.copyWith( - keywords: keywords, - ); - } - - _deleteKeyword(String keyword) { - final isContains = connectionsNotifier.value.keywords.contains(keyword); - if (!isContains) return; - final keywords = List.from(connectionsNotifier.value.keywords) - ..remove(keyword); - connectionsNotifier.value = connectionsNotifier.value.copyWith( - keywords: keywords, - ); - } - - _handleBlockConnection(String id) async { - clashCore.closeConnection(id); - connectionsNotifier.value = connectionsNotifier.value.copyWith( - connections: await clashCore.getConnections(), - ); - } - - @override - void dispose() { - timer?.cancel(); - connectionsNotifier.dispose(); - _scrollController.dispose(); - timer = null; - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, appState) => - appState.currentLabel == 'connections' || - appState.viewMode == ViewMode.mobile && - appState.currentLabel == "tools", - builder: (_, isCurrent, child) { - if (isCurrent == null || isCurrent) { - _initActions(); - } - return child!; - }, - child: ValueListenableBuilder( - valueListenable: connectionsNotifier, - builder: (_, state, __) { - var connections = state.filteredConnections; - if (connections.isEmpty) { - return NullStatus( - label: appLocalizations.nullConnectionsDesc, - ); - } - connections = connections.reversed.toList(); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (state.keywords.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Wrap( - runSpacing: 6, - spacing: 6, - children: [ - for (final keyword in state.keywords) - CommonChip( - label: keyword, - type: ChipType.delete, - onPressed: () { - _deleteKeyword(keyword); - }, - ), - ], - ), - ), - Expanded( - child: ListView.separated( - controller: _scrollController, - itemBuilder: (_, index) { - final connection = connections[index]; - return ConnectionItem( - key: Key(connection.id), - connection: connection, - onClick: _addKeyword, - trailing: IconButton( - icon: const Icon(Icons.block), - onPressed: () { - _handleBlockConnection(connection.id); - }, - ), - ); - }, - separatorBuilder: (BuildContext context, int index) { - return const Divider( - height: 0, - ); - }, - itemCount: connections.length, - ), - ) - ], - ); - }, - ), - ); - } -} - -class ConnectionsSearchDelegate extends SearchDelegate { - ValueNotifier connectionsNotifier; - - ConnectionsSearchDelegate({ - required ConnectionsAndKeywords state, - }) : connectionsNotifier = ValueNotifier(state); - - get state => connectionsNotifier.value; - - List get _results { - final lowerQuery = query.toLowerCase().trim(); - return connectionsNotifier.value.filteredConnections.where((request) { - final lowerNetwork = request.metadata.network.toLowerCase(); - final lowerHost = request.metadata.host.toLowerCase(); - final lowerDestinationIP = request.metadata.destinationIP.toLowerCase(); - final lowerProcess = request.metadata.process.toLowerCase(); - final lowerChains = request.chains.join("").toLowerCase(); - return lowerNetwork.contains(lowerQuery) || - lowerHost.contains(lowerQuery) || - lowerDestinationIP.contains(lowerQuery) || - lowerProcess.contains(lowerQuery) || - lowerChains.contains(lowerQuery); - }).toList(); - } - - _addKeyword(String keyword) { - final isContains = connectionsNotifier.value.keywords.contains(keyword); - if (isContains) return; - final keywords = List.from(connectionsNotifier.value.keywords) - ..add(keyword); - connectionsNotifier.value = connectionsNotifier.value.copyWith( - keywords: keywords, - ); - } - - _deleteKeyword(String keyword) { - final isContains = connectionsNotifier.value.keywords.contains(keyword); - if (!isContains) return; - final keywords = List.from(connectionsNotifier.value.keywords) - ..remove(keyword); - connectionsNotifier.value = connectionsNotifier.value.copyWith( - keywords: keywords, - ); - } - - _handleBlockConnection(String id) async { - clashCore.closeConnection(id); - connectionsNotifier.value = connectionsNotifier.value.copyWith( - connections: await clashCore.getConnections(), - ); - } - - @override - List? buildActions(BuildContext context) { - return [ - IconButton( - onPressed: () { - if (query.isEmpty) { - close(context, null); - return; - } - query = ''; - }, - icon: const Icon(Icons.clear), - ), - const SizedBox( - width: 8, - ) - ]; - } - - @override - Widget? buildLeading(BuildContext context) { - return IconButton( - onPressed: () { - close(context, null); - }, - icon: const Icon(Icons.arrow_back), - ); - } - - @override - Widget buildResults(BuildContext context) { - return buildSuggestions(context); - } - - @override - void dispose() { - connectionsNotifier.dispose(); - super.dispose(); - } - - @override - Widget buildSuggestions(BuildContext context) { - return ValueListenableBuilder( - valueListenable: connectionsNotifier, - builder: (_, __, ___) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (state.keywords.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Wrap( - runSpacing: 6, - spacing: 6, - children: [ - for (final keyword in state.keywords) - CommonChip( - label: keyword, - type: ChipType.delete, - onPressed: () { - _deleteKeyword(keyword); - }, - ), - ], - ), - ), - Expanded( - child: ListView.separated( - itemBuilder: (_, index) { - final connection = _results[index]; - return ConnectionItem( - key: Key(connection.id), - connection: connection, - onClick: _addKeyword, - trailing: IconButton( - icon: const Icon(Icons.block), - onPressed: () { - _handleBlockConnection(connection.id); - }, - ), - ); - }, - separatorBuilder: (BuildContext context, int index) { - return const Divider( - height: 0, - ); - }, - itemCount: _results.length, - ), - ) - ], - ); - }, - ); - } -} diff --git a/lib/fragments/dashboard/dashboard.dart b/lib/fragments/dashboard/dashboard.dart index ef587baa..e76f56f7 100644 --- a/lib/fragments/dashboard/dashboard.dart +++ b/lib/fragments/dashboard/dashboard.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/state.dart'; @@ -23,8 +24,7 @@ class _DashboardFragmentState extends State { return; } WidgetsBinding.instance.addPostFrameCallback((_) { - final commonScaffoldState = - context.findAncestorStateOfType(); + final commonScaffoldState = context.commonScaffoldState; commonScaffoldState?.floatingActionButton = const StartButton(); commonScaffoldState?.actions = [ ValueListenableBuilder( diff --git a/lib/fragments/fragments.dart b/lib/fragments/fragments.dart index 0a6a2edf..c3daf82e 100644 --- a/lib/fragments/fragments.dart +++ b/lib/fragments/fragments.dart @@ -3,11 +3,11 @@ export 'dashboard/dashboard.dart'; export 'tools.dart'; export 'profiles/profiles.dart'; export 'logs.dart'; -export 'connections.dart'; export 'access.dart'; export 'config/config.dart'; export 'application_setting.dart'; export 'about.dart'; export 'backup_and_recovery.dart'; export 'resources.dart'; -export 'requests.dart'; +export 'connection/requests.dart'; +export 'connection/connections.dart'; diff --git a/lib/fragments/logs.dart b/lib/fragments/logs.dart index 409b34ba..5f5ed1f0 100644 --- a/lib/fragments/logs.dart +++ b/lib/fragments/logs.dart @@ -1,12 +1,16 @@ import 'dart:async'; + import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; + import '../models/models.dart'; import '../widgets/widgets.dart'; +double _preOffset = 0; + class LogsFragment extends StatefulWidget { const LogsFragment({super.key}); @@ -14,48 +18,66 @@ class LogsFragment extends StatefulWidget { State createState() => _LogsFragmentState(); } -class _LogsFragmentState extends State { - final logsNotifier = ValueNotifier(const LogsAndKeywords()); - final scrollController = ScrollController( - keepScrollOffset: false, +class _LogsFragmentState extends State with ViewMixin { + final _logsStateNotifier = ValueNotifier(LogsState()); + final _scrollController = ScrollController( + initialScrollOffset: _preOffset != 0 ? _preOffset : double.maxFinite, ); + final FixedMap _cacheDynamicHeightMap = FixedMap(1000); + double _currentMaxWidth = 0; - Timer? timer; + List _logs = []; @override void initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - final appFlowingState = globalState.appController.appFlowingState; - logsNotifier.value = - logsNotifier.value.copyWith(logs: appFlowingState.logs); - if (timer != null) { - timer?.cancel(); - timer = null; - } - timer = Timer.periodic(const Duration(milliseconds: 200), (timer) { - final logs = appFlowingState.logs; - if (!logListEquality.equals( - logsNotifier.value.logs, - logs, - )) { - logsNotifier.value = logsNotifier.value.copyWith( - logs: logs, - ); - } - }); - }); + final appController = globalState.appController; + final appFlowingState = appController.appFlowingState; + _logsStateNotifier.value = _logsStateNotifier.value.copyWith( + logs: appFlowingState.logs, + ); } + @override + List get actions => [ + IconButton( + onPressed: () { + _handleExport(); + }, + icon: const Icon( + Icons.file_download_outlined, + ), + ), + ]; + + @override + get onSearch => (value) { + _logsStateNotifier.value = _logsStateNotifier.value.copyWith( + query: value, + ); + }; + + @override + get onKeywordsUpdate => (keywords) { + _logsStateNotifier.value = + _logsStateNotifier.value.copyWith(keywords: keywords); + }; + @override void dispose() { - timer?.cancel(); - logsNotifier.dispose(); - scrollController.dispose(); - timer = null; + _logsStateNotifier.dispose(); + _scrollController.dispose(); + _cacheDynamicHeightMap.clear(); super.dispose(); } + _handleTryClearCache(double maxWidth) { + if (_currentMaxWidth != maxWidth) { + _currentMaxWidth = maxWidth; + _cacheDynamicHeightMap.clear(); + } + } + _handleExport() async { final commonScaffoldState = context.commonScaffoldState; final res = await commonScaffoldState?.loadingRun( @@ -73,293 +95,151 @@ class _LogsFragmentState extends State { _initActions() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.actions = [ - IconButton( - onPressed: () { - showSearch( - context: context, - delegate: LogsSearchDelegate( - logs: logsNotifier.value, - ), - ); - }, - icon: const Icon(Icons.search), - ), - IconButton( - onPressed: () { - _handleExport(); - }, - icon: const Icon( - Icons.file_download_outlined, - ), - ), - ]; + super.initViewState(); }); } - _addKeyword(String keyword) { - final isContains = logsNotifier.value.keywords.contains(keyword); - if (isContains) return; - final keywords = List.from(logsNotifier.value.keywords) - ..add(keyword); - logsNotifier.value = logsNotifier.value.copyWith( - keywords: keywords, - ); - } - - _deleteKeyword(String keyword) { - final isContains = logsNotifier.value.keywords.contains(keyword); - if (!isContains) return; - final keywords = List.from(logsNotifier.value.keywords) - ..remove(keyword); - logsNotifier.value = logsNotifier.value.copyWith( - keywords: keywords, - ); - } - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, appState) => - appState.currentLabel == 'logs' || - appState.viewMode == ViewMode.mobile && - appState.currentLabel == "tools", - builder: (_, isCurrent, child) { - if (isCurrent == null || isCurrent) { - _initActions(); - } - return child!; - }, - child: ValueListenableBuilder( - valueListenable: logsNotifier, - builder: (_, state, __) { - final logs = state.filteredLogs; - if (logs.isEmpty) { - return NullStatus( - label: appLocalizations.nullLogsDesc, - ); - } - final reversedLogs = logs.reversed.toList(); - final logWidgets = reversedLogs - .map( - (log) => LogItem( - key: Key(log.dateTime.toString()), - log: log, - onClick: _addKeyword, - ), - ) - .separated( - const Divider( - height: 0, - ), - ) - .toList(); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (state.keywords.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Wrap( - runSpacing: 8, - spacing: 8, - children: [ - for (final keyword in state.keywords) - CommonChip( - label: keyword, - type: ChipType.delete, - onPressed: () { - _deleteKeyword(keyword); - }, - ), - ], - ), - ), - Expanded( - child: LayoutBuilder( - builder: (_, constraints) { - return ScrollConfiguration( - behavior: ShowBarScrollBehavior(), - child: ListView.builder( - controller: scrollController, - itemExtentBuilder: (index, __) { - final widget = logWidgets[index]; - if (widget.runtimeType == Divider) { - return 0; - } - final measure = globalState.measure; - final bodyLargeSize = measure.bodyLargeSize; - final bodySmallHeight = measure.bodySmallHeight; - final bodyMediumHeight = measure.bodyMediumHeight; - final log = reversedLogs[(index / 2).floor()]; - final width = (log.payload?.length ?? 0) * - bodyLargeSize.width + - 200; - final lines = (width / constraints.maxWidth).ceil(); - return lines * bodyLargeSize.height + - bodySmallHeight + - 8 + - bodyMediumHeight + - 40; - }, - itemBuilder: (_, index) { - return logWidgets[index]; - }, - itemCount: logWidgets.length, - )); - }, - ), - ) - ], - ); - }, + double _calcCacheHeight(String text) { + final cacheHeight = _cacheDynamicHeightMap.get(text); + if (cacheHeight != null) { + return cacheHeight; + } + final size = globalState.measure.computeTextSize( + Text( + text, + style: globalState.appController.context.textTheme.bodyLarge, ), + maxWidth: _currentMaxWidth, ); + _cacheDynamicHeightMap.put(text, size.height); + return size.height; } -} -class LogsSearchDelegate extends SearchDelegate { - ValueNotifier logsNotifier; - - LogsSearchDelegate({ - required LogsAndKeywords logs, - }) : logsNotifier = ValueNotifier(logs); - - @override - void dispose() { - logsNotifier.dispose(); - super.dispose(); - } - - get state => logsNotifier.value; - - List get _results { - final lowQuery = query.toLowerCase(); - return logsNotifier.value.filteredLogs - .where( - (log) => - (log.payload?.toLowerCase().contains(lowQuery) ?? false) || - log.logLevel.name.contains(lowQuery), - ) - .toList(); + double _getItemHeight(Log log) { + final measure = globalState.measure; + final bodySmallHeight = measure.bodySmallHeight; + final bodyMediumHeight = measure.bodyMediumHeight; + final height = _calcCacheHeight(log.payload ?? ""); + return height + bodySmallHeight + 8 + bodyMediumHeight + 40; } - @override - List? buildActions(BuildContext context) { - return [ - IconButton( - onPressed: () { - if (query.isEmpty) { - close(context, null); - return; - } - query = ''; - }, - icon: const Icon(Icons.clear), - ), - const SizedBox( - width: 8, - ) - ]; + updateLogsThrottler() { + throttler.call("logs", () { + final isEquality = logListEquality.equals( + _logs, + _logsStateNotifier.value.logs, + ); + if (isEquality) { + return; + } + WidgetsBinding.instance.addPostFrameCallback((_) { + _logsStateNotifier.value = _logsStateNotifier.value.copyWith( + logs: _logs, + ); + }); + }, duration: commonDuration); } - @override - Widget? buildLeading(BuildContext context) { - return IconButton( - onPressed: () { - close(context, null); + Widget _wrapLogsUpdate(Widget child) { + return Selector>( + selector: (_, appFlowingState) => appFlowingState.logs, + shouldRebuild: (prev, next) { + final isEquality = logListEquality.equals(prev, next); + if (!isEquality) { + _logs = next; + updateLogsThrottler(); + } + return !isEquality; }, - icon: const Icon(Icons.arrow_back), - ); - } - - @override - Widget buildResults(BuildContext context) { - return buildSuggestions(context); - } - - _addKeyword(String keyword) { - final isContains = logsNotifier.value.keywords.contains(keyword); - if (isContains) return; - final keywords = List.from(logsNotifier.value.keywords) - ..add(keyword); - logsNotifier.value = logsNotifier.value.copyWith( - keywords: keywords, - ); - } - - _deleteKeyword(String keyword) { - final isContains = logsNotifier.value.keywords.contains(keyword); - if (!isContains) return; - final keywords = List.from(logsNotifier.value.keywords) - ..remove(keyword); - logsNotifier.value = logsNotifier.value.copyWith( - keywords: keywords, + builder: (_, next, child) { + return child!; + }, + child: child, ); } @override - Widget buildSuggestions(BuildContext context) { - return ValueListenableBuilder( - valueListenable: logsNotifier, - builder: (_, __, ___) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (state.keywords.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Wrap( - runSpacing: 6, - spacing: 6, - children: [ - for (final keyword in state.keywords) - CommonChip( - label: keyword, - type: ChipType.delete, - onPressed: () { - _deleteKeyword(keyword); + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (_, constraints) { + _handleTryClearCache(constraints.maxWidth - 40); + return Selector( + selector: (_, appState) => + appState.currentLabel == 'logs' || + appState.viewMode == ViewMode.mobile && + appState.currentLabel == "tools", + builder: (_, isCurrent, child) { + if (isCurrent == null || isCurrent) { + _initActions(); + } + return child!; + }, + child: _wrapLogsUpdate( + Align( + alignment: Alignment.topCenter, + child: ValueListenableBuilder( + valueListenable: _logsStateNotifier, + builder: (_, state, __) { + final logs = state.list; + if (logs.isEmpty) { + return NullStatus( + label: appLocalizations.nullLogsDesc, + ); + } + final items = logs + .map( + (log) => LogItem( + key: Key(log.dateTime.toString()), + log: log, + onClick: (value) { + context.commonScaffoldState?.addKeyword(value); + }, + ), + ) + .separated( + const Divider( + height: 0, + ), + ) + .toList(); + return NotificationListener( + onNotification: (details) { + _preOffset = details.metrics.pixels; + return false; + }, + child: CommonScrollBar( + controller: _scrollController, + child: ListView.builder( + reverse: true, + shrinkWrap: true, + physics: NextClampingScrollPhysics(), + controller: _scrollController, + itemBuilder: (_, index) { + return items[index]; }, + itemExtentBuilder: (index, __) { + final item = items[index]; + if (item.runtimeType == Divider) { + return 0; + } + final log = logs[(index / 2).floor()]; + return _getItemHeight(log); + }, + itemCount: items.length, ), - ], - ), - ), - Expanded( - child: ListView.separated( - itemBuilder: (_, index) { - final log = _results[index]; - return LogItem( - key: Key(log.dateTime.toString()), - log: log, - onClick: (value) { - _addKeyword(value); - }, + ), ); }, - separatorBuilder: (BuildContext context, int index) { - return const Divider( - height: 0, - ); - }, - itemCount: _results.length, ), - ) - ], + ), + ), ); }, ); } } -class LogItem extends StatefulWidget { +class LogItem extends StatelessWidget { final Log log; final Function(String)? onClick; @@ -369,14 +249,8 @@ class LogItem extends StatefulWidget { this.onClick, }); - @override - State createState() => _LogItemState(); -} - -class _LogItemState extends State { @override Widget build(BuildContext context) { - final log = widget.log; return ListItem( padding: const EdgeInsets.symmetric( horizontal: 16, @@ -384,14 +258,16 @@ class _LogItemState extends State { ), title: SelectableText( log.payload ?? '', + style: context.textTheme.bodyLarge, ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SelectableText( "${log.dateTime}", - style: context.textTheme.bodySmall - ?.copyWith(color: context.colorScheme.primary), + style: context.textTheme.bodySmall?.copyWith( + color: context.colorScheme.primary, + ), ), const SizedBox( height: 8, @@ -400,8 +276,8 @@ class _LogItemState extends State { alignment: Alignment.centerLeft, child: CommonChip( onPressed: () { - if (widget.onClick == null) return; - widget.onClick!(log.logLevel.name); + if (onClick == null) return; + onClick!(log.logLevel.name); }, label: log.logLevel.name, ), @@ -411,3 +287,11 @@ class _LogItemState extends State { ); } } + +class NoGlowScrollBehavior extends ScrollBehavior { + @override + Widget buildOverscrollIndicator( + BuildContext context, Widget child, ScrollableDetails details) { + return child; // 禁用过度滚动效果 + } +} diff --git a/lib/fragments/profiles/profiles.dart b/lib/fragments/profiles/profiles.dart index 6a8aa410..f8de93f4 100644 --- a/lib/fragments/profiles/profiles.dart +++ b/lib/fragments/profiles/profiles.dart @@ -74,8 +74,7 @@ class _ProfilesFragmentState extends State { WidgetsBinding.instance.addPostFrameCallback( (_) { if (!mounted) return; - final commonScaffoldState = - context.findAncestorStateOfType(); + final commonScaffoldState = context.commonScaffoldState; commonScaffoldState?.actions = [ IconButton( onPressed: () { diff --git a/lib/fragments/proxies/common.dart b/lib/fragments/proxies/common.dart index 1cc3f279..4c6151d3 100644 --- a/lib/fragments/proxies/common.dart +++ b/lib/fragments/proxies/common.dart @@ -30,7 +30,7 @@ double get listHeaderHeight { double getItemHeight(ProxyCardType proxyCardType) { final measure = globalState.measure; final baseHeight = - 12 * 2 + measure.bodyMediumHeight * 2 + measure.bodySmallHeight + 8; + 12 * 2 + measure.bodyMediumHeight * 2 + measure.bodySmallHeight + 8 + 4; return switch (proxyCardType) { ProxyCardType.expand => baseHeight + measure.labelSmallHeight + 8, ProxyCardType.shrink => baseHeight, diff --git a/lib/fragments/proxies/list.dart b/lib/fragments/proxies/list.dart index 48c12d2e..8520fe06 100644 --- a/lib/fragments/proxies/list.dart +++ b/lib/fragments/proxies/list.dart @@ -273,13 +273,8 @@ class _ProxiesListFragmentState extends State { type: state.proxyCardType, ); final itemsOffset = _getItemHeightList(items, state.proxyCardType); - return Scrollbar( + return CommonScrollBar( controller: _controller, - thumbVisibility: true, - trackVisibility: true, - thickness: 8, - radius: const Radius.circular(8), - interactive: true, child: Stack( children: [ Positioned.fill( diff --git a/lib/fragments/proxies/providers.dart b/lib/fragments/proxies/providers.dart index 986a5554..95526c93 100644 --- a/lib/fragments/proxies/providers.dart +++ b/lib/fragments/proxies/providers.dart @@ -28,9 +28,7 @@ class _ProvidersState extends State { WidgetsBinding.instance.addPostFrameCallback( (_) { globalState.appController.updateProviders(); - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.actions = [ + context.commonScaffoldState?.actions = [ IconButton( onPressed: () { _updateProviders(); diff --git a/lib/fragments/proxies/proxies.dart b/lib/fragments/proxies/proxies.dart index bbfcb21e..aa42a1ca 100644 --- a/lib/fragments/proxies/proxies.dart +++ b/lib/fragments/proxies/proxies.dart @@ -21,10 +21,8 @@ class _ProxiesFragmentState extends State { final GlobalKey _proxiesTabKey = GlobalKey(); _initActions(ProxiesType proxiesType, bool hasProvider) { - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.actions = [ + WidgetsBinding.instance.addPostFrameCallback((_) { + context.commonScaffoldState?.actions = [ if (hasProvider) ...[ IconButton( onPressed: () { diff --git a/lib/fragments/proxies/tab.dart b/lib/fragments/proxies/tab.dart index 01144ffe..c8c05d25 100644 --- a/lib/fragments/proxies/tab.dart +++ b/lib/fragments/proxies/tab.dart @@ -320,9 +320,7 @@ class ProxyGroupViewState extends State { return; } WidgetsBinding.instance.addPostFrameCallback((_) { - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.floatingActionButton = DelayTestButton( + context.commonScaffoldState?.floatingActionButton = DelayTestButton( onClick: () async { await _delayTest(); }, diff --git a/lib/fragments/requests.dart b/lib/fragments/requests.dart deleted file mode 100644 index 53433c07..00000000 --- a/lib/fragments/requests.dart +++ /dev/null @@ -1,317 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/models/models.dart'; -import 'package:fl_clash/state.dart'; -import 'package:fl_clash/widgets/widgets.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class RequestsFragment extends StatefulWidget { - const RequestsFragment({super.key}); - - @override - State createState() => _RequestsFragmentState(); -} - -class _RequestsFragmentState extends State { - final requestsNotifier = - ValueNotifier(const ConnectionsAndKeywords()); - final ScrollController _scrollController = ScrollController( - keepScrollOffset: false, - ); - - Timer? timer; - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - final appState = globalState.appController.appState; - requestsNotifier.value = - requestsNotifier.value.copyWith(connections: appState.requests); - if (timer != null) { - timer?.cancel(); - timer = null; - } - timer = Timer.periodic(const Duration(milliseconds: 200), (timer) { - final maxLength = Platform.isAndroid ? 1000 : 60; - final requests = appState.requests.safeSublist( - appState.requests.length - maxLength, - ); - if (!connectionListEquality.equals( - requestsNotifier.value.connections, - requests, - )) { - requestsNotifier.value = - requestsNotifier.value.copyWith(connections: requests); - } - }); - }); - } - - _initActions() { - WidgetsBinding.instance.addPostFrameCallback( - (_) { - final commonScaffoldState = - context.findAncestorStateOfType(); - commonScaffoldState?.actions = [ - IconButton( - onPressed: () { - showSearch( - context: context, - delegate: RequestsSearchDelegate( - state: requestsNotifier.value, - ), - ); - }, - icon: const Icon(Icons.search), - ), - ]; - }, - ); - } - - _addKeyword(String keyword) { - final isContains = requestsNotifier.value.keywords.contains(keyword); - if (isContains) return; - final keywords = List.from(requestsNotifier.value.keywords) - ..add(keyword); - requestsNotifier.value = requestsNotifier.value.copyWith( - keywords: keywords, - ); - } - - _deleteKeyword(String keyword) { - final isContains = requestsNotifier.value.keywords.contains(keyword); - if (!isContains) return; - final keywords = List.from(requestsNotifier.value.keywords) - ..remove(keyword); - requestsNotifier.value = requestsNotifier.value.copyWith( - keywords: keywords, - ); - } - - @override - void dispose() { - timer?.cancel(); - _scrollController.dispose(); - timer = null; - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Selector( - selector: (_, appState) => - appState.currentLabel == 'requests' || - appState.viewMode == ViewMode.mobile && - appState.currentLabel == "tools", - builder: (_, isCurrent, child) { - if (isCurrent == null || isCurrent) { - _initActions(); - } - return child!; - }, - child: ValueListenableBuilder( - valueListenable: requestsNotifier, - builder: (_, state, __) { - var connections = state.filteredConnections; - if (connections.isEmpty) { - return NullStatus( - label: appLocalizations.nullRequestsDesc, - ); - } - connections = connections.reversed.toList(); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (state.keywords.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Wrap( - runSpacing: 6, - spacing: 6, - children: [ - for (final keyword in state.keywords) - CommonChip( - label: keyword, - type: ChipType.delete, - onPressed: () { - _deleteKeyword(keyword); - }, - ), - ], - ), - ), - Expanded( - child: ListView.separated( - controller: _scrollController, - itemBuilder: (_, index) { - final connection = connections[index]; - return ConnectionItem( - key: Key(connection.id), - connection: connection, - onClick: _addKeyword, - ); - }, - separatorBuilder: (BuildContext context, int index) { - return const Divider( - height: 0, - ); - }, - itemCount: connections.length, - ), - ) - ], - ); - }, - ), - ); - } -} - -class RequestsSearchDelegate extends SearchDelegate { - ValueNotifier requestsNotifier; - - RequestsSearchDelegate({ - required ConnectionsAndKeywords state, - }) : requestsNotifier = ValueNotifier(state); - - get state => requestsNotifier.value; - - List get _results { - final lowerQuery = query.toLowerCase().trim(); - return requestsNotifier.value.filteredConnections.where((request) { - final lowerNetwork = request.metadata.network.toLowerCase(); - final lowerHost = request.metadata.host.toLowerCase(); - final lowerDestinationIP = request.metadata.destinationIP.toLowerCase(); - final lowerProcess = request.metadata.process.toLowerCase(); - final lowerChains = request.chains.join("").toLowerCase(); - return lowerNetwork.contains(lowerQuery) || - lowerHost.contains(lowerQuery) || - lowerDestinationIP.contains(lowerQuery) || - lowerProcess.contains(lowerQuery) || - lowerChains.contains(lowerQuery); - }).toList(); - } - - _addKeyword(String keyword) { - final isContains = requestsNotifier.value.keywords.contains(keyword); - if (isContains) return; - final keywords = List.from(requestsNotifier.value.keywords) - ..add(keyword); - requestsNotifier.value = requestsNotifier.value.copyWith( - keywords: keywords, - ); - } - - _deleteKeyword(String keyword) { - final isContains = requestsNotifier.value.keywords.contains(keyword); - if (!isContains) return; - final keywords = List.from(requestsNotifier.value.keywords) - ..remove(keyword); - requestsNotifier.value = requestsNotifier.value.copyWith( - keywords: keywords, - ); - } - - @override - List? buildActions(BuildContext context) { - return [ - IconButton( - onPressed: () { - if (query.isEmpty) { - close(context, null); - return; - } - query = ''; - }, - icon: const Icon(Icons.clear), - ), - const SizedBox( - width: 8, - ) - ]; - } - - @override - Widget? buildLeading(BuildContext context) { - return IconButton( - onPressed: () { - close(context, null); - }, - icon: const Icon(Icons.arrow_back), - ); - } - - @override - Widget buildResults(BuildContext context) { - return buildSuggestions(context); - } - - @override - void dispose() { - requestsNotifier.dispose(); - super.dispose(); - } - - @override - Widget buildSuggestions(BuildContext context) { - return ValueListenableBuilder( - valueListenable: requestsNotifier, - builder: (_, __, ___) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (state.keywords.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 16, - ), - child: Wrap( - runSpacing: 6, - spacing: 6, - children: [ - for (final keyword in state.keywords) - CommonChip( - label: keyword, - type: ChipType.delete, - onPressed: () { - _deleteKeyword(keyword); - }, - ), - ], - ), - ), - Expanded( - child: ListView.separated( - itemBuilder: (_, index) { - final connection = _results[index]; - return ConnectionItem( - key: Key(connection.id), - connection: connection, - onClick: (value) { - _addKeyword(value); - }, - ); - }, - separatorBuilder: (BuildContext context, int index) { - return const Divider( - height: 0, - ); - }, - itemCount: _results.length, - ), - ) - ], - ); - }, - ); - } -} diff --git a/lib/fragments/resources.dart b/lib/fragments/resources.dart index ffc0be79..559fa73a 100644 --- a/lib/fragments/resources.dart +++ b/lib/fragments/resources.dart @@ -112,7 +112,7 @@ class _GeoDataListItemState extends State { } Future _getGeoFileLastModified(String fileName) async { - final homePath = await appPath.getHomeDirPath(); + final homePath = await appPath.homeDirPath; final file = File(join(homePath, fileName)); final lastModified = await file.lastModified(); final size = await file.length(); @@ -183,7 +183,12 @@ class _GeoDataListItemState extends State { } _handleUpdateGeoDataItem() async { - await globalState.safeRun(updateGeoDateItem); + await globalState.safeRun( + () async { + await updateGeoDateItem(); + }, + silence: false, + ); setState(() {}); } @@ -196,6 +201,7 @@ class _GeoDataListItemState extends State { geoType: geoItem.label, ), ); + print(message); if (message.isNotEmpty) throw message; } catch (e) { isUpdating.value = false; diff --git a/lib/fragments/tools.dart b/lib/fragments/tools.dart index ef5031ea..d99f64f6 100644 --- a/lib/fragments/tools.dart +++ b/lib/fragments/tools.dart @@ -35,6 +35,7 @@ class _ToolboxFragmentState extends State { delegate: OpenDelegate( title: Intl.message(navigationItem.label), widget: navigationItem.fragment, + extendPageWidth: 360, ), ); } @@ -195,14 +196,17 @@ class _ToolboxFragmentState extends State { Selector( selector: (_, appState) { return MoreToolsSelectorState( - navigationItems: appState.viewMode == ViewMode.mobile - ? appState.navigationItems.where( - (element) { - return element.modes - .contains(NavigationItemMode.more); - }, - ).toList() - : [], + navigationItems: appState.navigationItems.where((element) { + final isMore = + element.modes.contains(NavigationItemMode.more); + final isDesktop = + element.modes.contains(NavigationItemMode.desktop); + if (isMore && !isDesktop) return true; + if (appState.viewMode != ViewMode.mobile || !isMore) { + return false; + } + return true; + }).toList(), ); }, builder: (_, state, __) { diff --git a/lib/l10n/arb/intl_en.arb b/lib/l10n/arb/intl_en.arb index fc888f53..baf26cb8 100644 --- a/lib/l10n/arb/intl_en.arb +++ b/lib/l10n/arb/intl_en.arb @@ -341,5 +341,6 @@ "nullProxies": "No proxies", "copySuccess": "Copy success", "copyLink": "Copy link", - "exportFile": "Export file" + "exportFile": "Export file", + "cacheCorrupt": "The cache is corrupt. Do you want to clear it?" } \ No newline at end of file diff --git a/lib/l10n/arb/intl_zh_CN.arb b/lib/l10n/arb/intl_zh_CN.arb index db8941a5..5ed858dc 100644 --- a/lib/l10n/arb/intl_zh_CN.arb +++ b/lib/l10n/arb/intl_zh_CN.arb @@ -341,5 +341,6 @@ "nullProxies": "暂无代理", "copySuccess": "复制成功", "copyLink": "复制链接", - "exportFile": "导出文件" + "exportFile": "导出文件", + "cacheCorrupt": "缓存已损坏,是否清空?" } diff --git a/lib/l10n/intl/messages_all.dart b/lib/l10n/intl/messages_all.dart index bb98700c..24e4beb9 100644 --- a/lib/l10n/intl/messages_all.dart +++ b/lib/l10n/intl/messages_all.dart @@ -39,8 +39,10 @@ MessageLookupByLibrary? _findExact(String localeName) { /// User programs should call this before using [localeName] for messages. Future initializeMessages(String localeName) { var availableLocale = Intl.verifiedLocale( - localeName, (locale) => _deferredLibraries[locale] != null, - onFailure: (_) => null); + localeName, + (locale) => _deferredLibraries[locale] != null, + onFailure: (_) => null, + ); if (availableLocale == null) { return new SynchronousFuture(false); } @@ -60,8 +62,11 @@ bool _messagesExistFor(String locale) { } MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { - var actualLocale = - Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); + var actualLocale = Intl.verifiedLocale( + locale, + _messagesExistFor, + onFailure: (_) => null, + ); if (actualLocale == null) return null; return _findExact(actualLocale); } diff --git a/lib/l10n/intl/messages_en.dart b/lib/l10n/intl/messages_en.dart index b31a1fae..d6ad39e6 100644 --- a/lib/l10n/intl/messages_en.dart +++ b/lib/l10n/intl/messages_en.dart @@ -22,491 +22,593 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "about": MessageLookupByLibrary.simpleMessage("About"), - "accessControl": MessageLookupByLibrary.simpleMessage("AccessControl"), - "accessControlAllowDesc": MessageLookupByLibrary.simpleMessage( - "Only allow selected app to enter VPN"), - "accessControlDesc": MessageLookupByLibrary.simpleMessage( - "Configure application access proxy"), - "accessControlNotAllowDesc": MessageLookupByLibrary.simpleMessage( - "The selected application will be excluded from VPN"), - "account": MessageLookupByLibrary.simpleMessage("Account"), - "accountTip": - MessageLookupByLibrary.simpleMessage("Account cannot be empty"), - "action": MessageLookupByLibrary.simpleMessage("Action"), - "action_mode": MessageLookupByLibrary.simpleMessage("Switch mode"), - "action_proxy": MessageLookupByLibrary.simpleMessage("System proxy"), - "action_start": MessageLookupByLibrary.simpleMessage("Start/Stop"), - "action_tun": MessageLookupByLibrary.simpleMessage("TUN"), - "action_view": MessageLookupByLibrary.simpleMessage("Show/Hide"), - "add": MessageLookupByLibrary.simpleMessage("Add"), - "address": MessageLookupByLibrary.simpleMessage("Address"), - "addressHelp": - MessageLookupByLibrary.simpleMessage("WebDAV server address"), - "addressTip": MessageLookupByLibrary.simpleMessage( - "Please enter a valid WebDAV address"), - "adminAutoLaunch": - MessageLookupByLibrary.simpleMessage("Admin auto launch"), - "adminAutoLaunchDesc": - MessageLookupByLibrary.simpleMessage("Boot up by using admin mode"), - "ago": MessageLookupByLibrary.simpleMessage(" Ago"), - "agree": MessageLookupByLibrary.simpleMessage("Agree"), - "allApps": MessageLookupByLibrary.simpleMessage("All apps"), - "allowBypass": MessageLookupByLibrary.simpleMessage( - "Allow applications to bypass VPN"), - "allowBypassDesc": MessageLookupByLibrary.simpleMessage( - "Some apps can bypass VPN when turned on"), - "allowLan": MessageLookupByLibrary.simpleMessage("AllowLan"), - "allowLanDesc": MessageLookupByLibrary.simpleMessage( - "Allow access proxy through the LAN"), - "app": MessageLookupByLibrary.simpleMessage("App"), - "appAccessControl": - MessageLookupByLibrary.simpleMessage("App access control"), - "appDesc": MessageLookupByLibrary.simpleMessage( - "Processing app related settings"), - "application": MessageLookupByLibrary.simpleMessage("Application"), - "applicationDesc": MessageLookupByLibrary.simpleMessage( - "Modify application related settings"), - "auto": MessageLookupByLibrary.simpleMessage("Auto"), - "autoCheckUpdate": - MessageLookupByLibrary.simpleMessage("Auto check updates"), - "autoCheckUpdateDesc": MessageLookupByLibrary.simpleMessage( - "Auto check for updates when the app starts"), - "autoCloseConnections": - MessageLookupByLibrary.simpleMessage("Auto lose connections"), - "autoCloseConnectionsDesc": MessageLookupByLibrary.simpleMessage( - "Auto close connections after change node"), - "autoLaunch": MessageLookupByLibrary.simpleMessage("Auto launch"), - "autoLaunchDesc": MessageLookupByLibrary.simpleMessage( - "Follow the system self startup"), - "autoRun": MessageLookupByLibrary.simpleMessage("AutoRun"), - "autoRunDesc": MessageLookupByLibrary.simpleMessage( - "Auto run when the application is opened"), - "autoUpdate": MessageLookupByLibrary.simpleMessage("Auto update"), - "autoUpdateInterval": MessageLookupByLibrary.simpleMessage( - "Auto update interval (minutes)"), - "backup": MessageLookupByLibrary.simpleMessage("Backup"), - "backupAndRecovery": - MessageLookupByLibrary.simpleMessage("Backup and Recovery"), - "backupAndRecoveryDesc": MessageLookupByLibrary.simpleMessage( - "Sync data via WebDAV or file"), - "backupSuccess": MessageLookupByLibrary.simpleMessage("Backup success"), - "bind": MessageLookupByLibrary.simpleMessage("Bind"), - "blacklistMode": MessageLookupByLibrary.simpleMessage("Blacklist mode"), - "bypassDomain": MessageLookupByLibrary.simpleMessage("Bypass domain"), - "bypassDomainDesc": MessageLookupByLibrary.simpleMessage( - "Only takes effect when the system proxy is enabled"), - "cancel": MessageLookupByLibrary.simpleMessage("Cancel"), - "cancelFilterSystemApp": - MessageLookupByLibrary.simpleMessage("Cancel filter system app"), - "cancelSelectAll": - MessageLookupByLibrary.simpleMessage("Cancel select all"), - "checkError": MessageLookupByLibrary.simpleMessage("Check error"), - "checkUpdate": - MessageLookupByLibrary.simpleMessage("Check for updates"), - "checkUpdateError": MessageLookupByLibrary.simpleMessage( - "The current application is already the latest version"), - "checking": MessageLookupByLibrary.simpleMessage("Checking..."), - "clipboardExport": - MessageLookupByLibrary.simpleMessage("Export clipboard"), - "clipboardImport": - MessageLookupByLibrary.simpleMessage("Clipboard import"), - "columns": MessageLookupByLibrary.simpleMessage("Columns"), - "compatible": - MessageLookupByLibrary.simpleMessage("Compatibility mode"), - "compatibleDesc": MessageLookupByLibrary.simpleMessage( - "Opening it will lose part of its application ability and gain the support of full amount of Clash."), - "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), - "connections": MessageLookupByLibrary.simpleMessage("Connections"), - "connectionsDesc": MessageLookupByLibrary.simpleMessage( - "View current connections data"), - "connectivity": MessageLookupByLibrary.simpleMessage("Connectivity:"), - "copy": MessageLookupByLibrary.simpleMessage("Copy"), - "copyEnvVar": MessageLookupByLibrary.simpleMessage( - "Copying environment variables"), - "copyLink": MessageLookupByLibrary.simpleMessage("Copy link"), - "copySuccess": MessageLookupByLibrary.simpleMessage("Copy success"), - "core": MessageLookupByLibrary.simpleMessage("Core"), - "coreInfo": MessageLookupByLibrary.simpleMessage("Core info"), - "country": MessageLookupByLibrary.simpleMessage("Country"), - "create": MessageLookupByLibrary.simpleMessage("Create"), - "cut": MessageLookupByLibrary.simpleMessage("Cut"), - "dark": MessageLookupByLibrary.simpleMessage("Dark"), - "dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"), - "days": MessageLookupByLibrary.simpleMessage("Days"), - "defaultNameserver": - MessageLookupByLibrary.simpleMessage("Default nameserver"), - "defaultNameserverDesc": - MessageLookupByLibrary.simpleMessage("For resolving DNS server"), - "defaultSort": MessageLookupByLibrary.simpleMessage("Sort by default"), - "defaultText": MessageLookupByLibrary.simpleMessage("Default"), - "delay": MessageLookupByLibrary.simpleMessage("Delay"), - "delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"), - "delete": MessageLookupByLibrary.simpleMessage("Delete"), - "deleteProfileTip": MessageLookupByLibrary.simpleMessage( - "Sure you want to delete the current profile?"), - "desc": MessageLookupByLibrary.simpleMessage( - "A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free."), - "direct": MessageLookupByLibrary.simpleMessage("Direct"), - "disclaimer": MessageLookupByLibrary.simpleMessage("Disclaimer"), - "disclaimerDesc": MessageLookupByLibrary.simpleMessage( - "This software is only used for non-commercial purposes such as learning exchanges and scientific research. It is strictly prohibited to use this software for commercial purposes. Any commercial activity, if any, has nothing to do with this software."), - "discoverNewVersion": - MessageLookupByLibrary.simpleMessage("Discover the new version"), - "discovery": - MessageLookupByLibrary.simpleMessage("Discovery a new version"), - "dnsDesc": - MessageLookupByLibrary.simpleMessage("Update DNS related settings"), - "dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"), - "doYouWantToPass": - MessageLookupByLibrary.simpleMessage("Do you want to pass"), - "domain": MessageLookupByLibrary.simpleMessage("Domain"), - "download": MessageLookupByLibrary.simpleMessage("Download"), - "edit": MessageLookupByLibrary.simpleMessage("Edit"), - "en": MessageLookupByLibrary.simpleMessage("English"), - "entries": MessageLookupByLibrary.simpleMessage(" entries"), - "exclude": - MessageLookupByLibrary.simpleMessage("Hidden from recent tasks"), - "excludeDesc": MessageLookupByLibrary.simpleMessage( - "When the app is in the background, the app is hidden from the recent task"), - "exit": MessageLookupByLibrary.simpleMessage("Exit"), - "expand": MessageLookupByLibrary.simpleMessage("Standard"), - "expirationTime": - MessageLookupByLibrary.simpleMessage("Expiration time"), - "exportFile": MessageLookupByLibrary.simpleMessage("Export file"), - "exportLogs": MessageLookupByLibrary.simpleMessage("Export logs"), - "exportSuccess": MessageLookupByLibrary.simpleMessage("Export Success"), - "externalController": - MessageLookupByLibrary.simpleMessage("ExternalController"), - "externalControllerDesc": MessageLookupByLibrary.simpleMessage( - "Once enabled, the Clash kernel can be controlled on port 9090"), - "externalLink": MessageLookupByLibrary.simpleMessage("External link"), - "externalResources": - MessageLookupByLibrary.simpleMessage("External resources"), - "fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip filter"), - "fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip range"), - "fallback": MessageLookupByLibrary.simpleMessage("Fallback"), - "fallbackDesc": - MessageLookupByLibrary.simpleMessage("Generally use offshore DNS"), - "fallbackFilter": - MessageLookupByLibrary.simpleMessage("Fallback filter"), - "file": MessageLookupByLibrary.simpleMessage("File"), - "fileDesc": - MessageLookupByLibrary.simpleMessage("Directly upload profile"), - "fileIsUpdate": MessageLookupByLibrary.simpleMessage( - "The file has been modified. Do you want to save the changes?"), - "filterSystemApp": - MessageLookupByLibrary.simpleMessage("Filter system app"), - "findProcessMode": MessageLookupByLibrary.simpleMessage("Find process"), - "findProcessModeDesc": MessageLookupByLibrary.simpleMessage( - "There is a risk of flashback after opening"), - "fontFamily": MessageLookupByLibrary.simpleMessage("FontFamily"), - "fourColumns": MessageLookupByLibrary.simpleMessage("Four columns"), - "general": MessageLookupByLibrary.simpleMessage("General"), - "generalDesc": - MessageLookupByLibrary.simpleMessage("Overwrite general settings"), - "geoData": MessageLookupByLibrary.simpleMessage("GeoData"), - "geodataLoader": - MessageLookupByLibrary.simpleMessage("Geo Low Memory Mode"), - "geodataLoaderDesc": MessageLookupByLibrary.simpleMessage( - "Enabling will use the Geo low memory loader"), - "geoipCode": MessageLookupByLibrary.simpleMessage("Geoip code"), - "global": MessageLookupByLibrary.simpleMessage("Global"), - "go": MessageLookupByLibrary.simpleMessage("Go"), - "goDownload": MessageLookupByLibrary.simpleMessage("Go to download"), - "hasCacheChange": MessageLookupByLibrary.simpleMessage( - "Do you want to cache the changes?"), - "hostsDesc": MessageLookupByLibrary.simpleMessage("Add Hosts"), - "hotkeyConflict": - MessageLookupByLibrary.simpleMessage("Hotkey conflict"), - "hotkeyManagement": - MessageLookupByLibrary.simpleMessage("Hotkey Management"), - "hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage( - "Use keyboard to control applications"), - "hours": MessageLookupByLibrary.simpleMessage("Hours"), - "icon": MessageLookupByLibrary.simpleMessage("Icon"), - "iconConfiguration": - MessageLookupByLibrary.simpleMessage("Icon configuration"), - "iconStyle": MessageLookupByLibrary.simpleMessage("Icon style"), - "importFromURL": - MessageLookupByLibrary.simpleMessage("Import from URL"), - "infiniteTime": - MessageLookupByLibrary.simpleMessage("Long term effective"), - "init": MessageLookupByLibrary.simpleMessage("Init"), - "inputCorrectHotkey": MessageLookupByLibrary.simpleMessage( - "Please enter the correct hotkey"), - "intelligentSelected": - MessageLookupByLibrary.simpleMessage("Intelligent selection"), - "intranetIP": MessageLookupByLibrary.simpleMessage("Intranet IP"), - "ipcidr": MessageLookupByLibrary.simpleMessage("Ipcidr"), - "ipv6Desc": MessageLookupByLibrary.simpleMessage( - "When turned on it will be able to receive IPv6 traffic"), - "ipv6InboundDesc": - MessageLookupByLibrary.simpleMessage("Allow IPv6 inbound"), - "just": MessageLookupByLibrary.simpleMessage("Just"), - "keepAliveIntervalDesc": - MessageLookupByLibrary.simpleMessage("Tcp keep alive interval"), - "key": MessageLookupByLibrary.simpleMessage("Key"), - "language": MessageLookupByLibrary.simpleMessage("Language"), - "layout": MessageLookupByLibrary.simpleMessage("Layout"), - "light": MessageLookupByLibrary.simpleMessage("Light"), - "list": MessageLookupByLibrary.simpleMessage("List"), - "local": MessageLookupByLibrary.simpleMessage("Local"), - "localBackupDesc": - MessageLookupByLibrary.simpleMessage("Backup local data to local"), - "localRecoveryDesc": - MessageLookupByLibrary.simpleMessage("Recovery data from file"), - "logLevel": MessageLookupByLibrary.simpleMessage("LogLevel"), - "logcat": MessageLookupByLibrary.simpleMessage("Logcat"), - "logcatDesc": MessageLookupByLibrary.simpleMessage( - "Disabling will hide the log entry"), - "logs": MessageLookupByLibrary.simpleMessage("Logs"), - "logsDesc": MessageLookupByLibrary.simpleMessage("Log capture records"), - "loopback": - MessageLookupByLibrary.simpleMessage("Loopback unlock tool"), - "loopbackDesc": MessageLookupByLibrary.simpleMessage( - "Used for UWP loopback unlocking"), - "loose": MessageLookupByLibrary.simpleMessage("Loose"), - "memoryInfo": MessageLookupByLibrary.simpleMessage("Memory info"), - "min": MessageLookupByLibrary.simpleMessage("Min"), - "minimizeOnExit": - MessageLookupByLibrary.simpleMessage("Minimize on exit"), - "minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage( - "Modify the default system exit event"), - "minutes": MessageLookupByLibrary.simpleMessage("Minutes"), - "mode": MessageLookupByLibrary.simpleMessage("Mode"), - "months": MessageLookupByLibrary.simpleMessage("Months"), - "more": MessageLookupByLibrary.simpleMessage("More"), - "name": MessageLookupByLibrary.simpleMessage("Name"), - "nameSort": MessageLookupByLibrary.simpleMessage("Sort by name"), - "nameserver": MessageLookupByLibrary.simpleMessage("Nameserver"), - "nameserverDesc": - MessageLookupByLibrary.simpleMessage("For resolving domain"), - "nameserverPolicy": - MessageLookupByLibrary.simpleMessage("Nameserver policy"), - "nameserverPolicyDesc": MessageLookupByLibrary.simpleMessage( - "Specify the corresponding nameserver policy"), - "network": MessageLookupByLibrary.simpleMessage("Network"), - "networkDesc": MessageLookupByLibrary.simpleMessage( - "Modify network-related settings"), - "networkDetection": - MessageLookupByLibrary.simpleMessage("Network detection"), - "networkSpeed": MessageLookupByLibrary.simpleMessage("Network speed"), - "noData": MessageLookupByLibrary.simpleMessage("No data"), - "noHotKey": MessageLookupByLibrary.simpleMessage("No HotKey"), - "noIcon": MessageLookupByLibrary.simpleMessage("None"), - "noInfo": MessageLookupByLibrary.simpleMessage("No info"), - "noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("No more info"), - "noNetwork": MessageLookupByLibrary.simpleMessage("No network"), - "noProxy": MessageLookupByLibrary.simpleMessage("No proxy"), - "noProxyDesc": MessageLookupByLibrary.simpleMessage( - "Please create a profile or add a valid profile"), - "notEmpty": MessageLookupByLibrary.simpleMessage("Cannot be empty"), - "notSelectedTip": MessageLookupByLibrary.simpleMessage( - "The current proxy group cannot be selected."), - "nullConnectionsDesc": - MessageLookupByLibrary.simpleMessage("No connections"), - "nullCoreInfoDesc": - MessageLookupByLibrary.simpleMessage("Unable to obtain core info"), - "nullLogsDesc": MessageLookupByLibrary.simpleMessage("No logs"), - "nullProfileDesc": MessageLookupByLibrary.simpleMessage( - "No profile, Please add a profile"), - "nullProxies": MessageLookupByLibrary.simpleMessage("No proxies"), - "nullRequestsDesc": MessageLookupByLibrary.simpleMessage("No requests"), - "oneColumn": MessageLookupByLibrary.simpleMessage("One column"), - "onlyIcon": MessageLookupByLibrary.simpleMessage("Icon"), - "onlyOtherApps": - MessageLookupByLibrary.simpleMessage("Only third-party apps"), - "onlyStatisticsProxy": - MessageLookupByLibrary.simpleMessage("Only statistics proxy"), - "onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage( - "When turned on, only statistics proxy traffic"), - "options": MessageLookupByLibrary.simpleMessage("Options"), - "other": MessageLookupByLibrary.simpleMessage("Other"), - "otherContributors": - MessageLookupByLibrary.simpleMessage("Other contributors"), - "outboundMode": MessageLookupByLibrary.simpleMessage("Outbound mode"), - "override": MessageLookupByLibrary.simpleMessage("Override"), - "overrideDesc": MessageLookupByLibrary.simpleMessage( - "Override Proxy related config"), - "overrideDns": MessageLookupByLibrary.simpleMessage("Override Dns"), - "overrideDnsDesc": MessageLookupByLibrary.simpleMessage( - "Turning it on will override the DNS options in the profile"), - "password": MessageLookupByLibrary.simpleMessage("Password"), - "passwordTip": - MessageLookupByLibrary.simpleMessage("Password cannot be empty"), - "paste": MessageLookupByLibrary.simpleMessage("Paste"), - "pleaseBindWebDAV": - MessageLookupByLibrary.simpleMessage("Please bind WebDAV"), - "pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage( - "Please enter the admin password"), - "pleaseUploadFile": - MessageLookupByLibrary.simpleMessage("Please upload file"), - "pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage( - "Please upload a valid QR code"), - "port": MessageLookupByLibrary.simpleMessage("Port"), - "preferH3Desc": MessageLookupByLibrary.simpleMessage( - "Prioritize the use of DOH\'s http/3"), - "pressKeyboard": - MessageLookupByLibrary.simpleMessage("Please press the keyboard."), - "preview": MessageLookupByLibrary.simpleMessage("Preview"), - "profile": MessageLookupByLibrary.simpleMessage("Profile"), - "profileAutoUpdateIntervalInvalidValidationDesc": - MessageLookupByLibrary.simpleMessage( - "Please input a valid interval time format"), - "profileAutoUpdateIntervalNullValidationDesc": - MessageLookupByLibrary.simpleMessage( - "Please enter the auto update interval time"), - "profileHasUpdate": MessageLookupByLibrary.simpleMessage( - "The profile has been modified. Do you want to disable auto update?"), - "profileNameNullValidationDesc": MessageLookupByLibrary.simpleMessage( - "Please input the profile name"), - "profileParseErrorDesc": - MessageLookupByLibrary.simpleMessage("profile parse error"), - "profileUrlInvalidValidationDesc": MessageLookupByLibrary.simpleMessage( - "Please input a valid profile URL"), - "profileUrlNullValidationDesc": MessageLookupByLibrary.simpleMessage( - "Please input the profile URL"), - "profiles": MessageLookupByLibrary.simpleMessage("Profiles"), - "profilesSort": MessageLookupByLibrary.simpleMessage("Profiles sort"), - "project": MessageLookupByLibrary.simpleMessage("Project"), - "providers": MessageLookupByLibrary.simpleMessage("Providers"), - "proxies": MessageLookupByLibrary.simpleMessage("Proxies"), - "proxiesSetting": - MessageLookupByLibrary.simpleMessage("Proxies setting"), - "proxyGroup": MessageLookupByLibrary.simpleMessage("Proxy group"), - "proxyNameserver": - MessageLookupByLibrary.simpleMessage("Proxy nameserver"), - "proxyNameserverDesc": MessageLookupByLibrary.simpleMessage( - "Domain for resolving proxy nodes"), - "proxyPort": MessageLookupByLibrary.simpleMessage("ProxyPort"), - "proxyPortDesc": MessageLookupByLibrary.simpleMessage( - "Set the Clash listening port"), - "proxyProviders": - MessageLookupByLibrary.simpleMessage("Proxy providers"), - "prueBlackMode": - MessageLookupByLibrary.simpleMessage("Prue black mode"), - "qrcode": MessageLookupByLibrary.simpleMessage("QR code"), - "qrcodeDesc": MessageLookupByLibrary.simpleMessage( - "Scan QR code to obtain profile"), - "recovery": MessageLookupByLibrary.simpleMessage("Recovery"), - "recoveryAll": - MessageLookupByLibrary.simpleMessage("Recovery all data"), - "recoveryProfiles": - MessageLookupByLibrary.simpleMessage("Only recovery profiles"), - "recoverySuccess": - MessageLookupByLibrary.simpleMessage("Recovery success"), - "regExp": MessageLookupByLibrary.simpleMessage("RegExp"), - "remote": MessageLookupByLibrary.simpleMessage("Remote"), - "remoteBackupDesc": - MessageLookupByLibrary.simpleMessage("Backup local data to WebDAV"), - "remoteRecoveryDesc": - MessageLookupByLibrary.simpleMessage("Recovery data from WebDAV"), - "remove": MessageLookupByLibrary.simpleMessage("Remove"), - "requests": MessageLookupByLibrary.simpleMessage("Requests"), - "requestsDesc": MessageLookupByLibrary.simpleMessage( - "View recently request records"), - "reset": MessageLookupByLibrary.simpleMessage("Reset"), - "resetTip": MessageLookupByLibrary.simpleMessage("Make sure to reset"), - "resources": MessageLookupByLibrary.simpleMessage("Resources"), - "resourcesDesc": MessageLookupByLibrary.simpleMessage( - "External resource related info"), - "respectRules": MessageLookupByLibrary.simpleMessage("Respect rules"), - "respectRulesDesc": MessageLookupByLibrary.simpleMessage( - "DNS connection following rules, need to configure proxy-server-nameserver"), - "routeAddress": MessageLookupByLibrary.simpleMessage("Route address"), - "routeAddressDesc": - MessageLookupByLibrary.simpleMessage("Config listen route address"), - "routeMode": MessageLookupByLibrary.simpleMessage("Route mode"), - "routeMode_bypassPrivate": MessageLookupByLibrary.simpleMessage( - "Bypass private route address"), - "routeMode_config": MessageLookupByLibrary.simpleMessage("Use config"), - "rule": MessageLookupByLibrary.simpleMessage("Rule"), - "ruleProviders": MessageLookupByLibrary.simpleMessage("Rule providers"), - "save": MessageLookupByLibrary.simpleMessage("Save"), - "search": MessageLookupByLibrary.simpleMessage("Search"), - "seconds": MessageLookupByLibrary.simpleMessage("Seconds"), - "selectAll": MessageLookupByLibrary.simpleMessage("Select all"), - "selected": MessageLookupByLibrary.simpleMessage("Selected"), - "settings": MessageLookupByLibrary.simpleMessage("Settings"), - "show": MessageLookupByLibrary.simpleMessage("Show"), - "shrink": MessageLookupByLibrary.simpleMessage("Shrink"), - "silentLaunch": MessageLookupByLibrary.simpleMessage("SilentLaunch"), - "silentLaunchDesc": - MessageLookupByLibrary.simpleMessage("Start in the background"), - "size": MessageLookupByLibrary.simpleMessage("Size"), - "sort": MessageLookupByLibrary.simpleMessage("Sort"), - "source": MessageLookupByLibrary.simpleMessage("Source"), - "stackMode": MessageLookupByLibrary.simpleMessage("Stack mode"), - "standard": MessageLookupByLibrary.simpleMessage("Standard"), - "start": MessageLookupByLibrary.simpleMessage("Start"), - "startVpn": MessageLookupByLibrary.simpleMessage("Starting VPN..."), - "status": MessageLookupByLibrary.simpleMessage("Status"), - "statusDesc": MessageLookupByLibrary.simpleMessage( - "System DNS will be used when turned off"), - "stop": MessageLookupByLibrary.simpleMessage("Stop"), - "stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."), - "style": MessageLookupByLibrary.simpleMessage("Style"), - "submit": MessageLookupByLibrary.simpleMessage("Submit"), - "sync": MessageLookupByLibrary.simpleMessage("Sync"), - "system": MessageLookupByLibrary.simpleMessage("System"), - "systemFont": MessageLookupByLibrary.simpleMessage("System font"), - "systemProxy": MessageLookupByLibrary.simpleMessage("System proxy"), - "systemProxyDesc": MessageLookupByLibrary.simpleMessage( - "Attach HTTP proxy to VpnService"), - "tab": MessageLookupByLibrary.simpleMessage("Tab"), - "tabAnimation": MessageLookupByLibrary.simpleMessage("Tab animation"), - "tabAnimationDesc": MessageLookupByLibrary.simpleMessage( - "When enabled, the home tab will add a toggle animation"), - "tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP concurrent"), - "tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage( - "Enabling it will allow TCP concurrency"), - "testUrl": MessageLookupByLibrary.simpleMessage("Test url"), - "theme": MessageLookupByLibrary.simpleMessage("Theme"), - "themeColor": MessageLookupByLibrary.simpleMessage("Theme color"), - "themeDesc": MessageLookupByLibrary.simpleMessage( - "Set dark mode,adjust the color"), - "themeMode": MessageLookupByLibrary.simpleMessage("Theme mode"), - "threeColumns": MessageLookupByLibrary.simpleMessage("Three columns"), - "tight": MessageLookupByLibrary.simpleMessage("Tight"), - "time": MessageLookupByLibrary.simpleMessage("Time"), - "tip": MessageLookupByLibrary.simpleMessage("tip"), - "toggle": MessageLookupByLibrary.simpleMessage("Toggle"), - "tools": MessageLookupByLibrary.simpleMessage("Tools"), - "trafficUsage": MessageLookupByLibrary.simpleMessage("Traffic usage"), - "tun": MessageLookupByLibrary.simpleMessage("TUN"), - "tunDesc": MessageLookupByLibrary.simpleMessage( - "only effective in administrator mode"), - "twoColumns": MessageLookupByLibrary.simpleMessage("Two columns"), - "unableToUpdateCurrentProfileDesc": - MessageLookupByLibrary.simpleMessage( - "unable to update current profile"), - "unifiedDelay": MessageLookupByLibrary.simpleMessage("Unified delay"), - "unifiedDelayDesc": MessageLookupByLibrary.simpleMessage( - "Remove extra delays such as handshaking"), - "unknown": MessageLookupByLibrary.simpleMessage("Unknown"), - "update": MessageLookupByLibrary.simpleMessage("Update"), - "upload": MessageLookupByLibrary.simpleMessage("Upload"), - "url": MessageLookupByLibrary.simpleMessage("URL"), - "urlDesc": - MessageLookupByLibrary.simpleMessage("Obtain profile through URL"), - "useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"), - "useSystemHosts": - MessageLookupByLibrary.simpleMessage("Use system hosts"), - "value": MessageLookupByLibrary.simpleMessage("Value"), - "view": MessageLookupByLibrary.simpleMessage("View"), - "vpnDesc": - MessageLookupByLibrary.simpleMessage("Modify VPN related settings"), - "vpnEnableDesc": MessageLookupByLibrary.simpleMessage( - "Auto routes all system traffic through VpnService"), - "vpnSystemProxyDesc": MessageLookupByLibrary.simpleMessage( - "Attach HTTP proxy to VpnService"), - "vpnTip": MessageLookupByLibrary.simpleMessage( - "Changes take effect after restarting the VPN"), - "webDAVConfiguration": - MessageLookupByLibrary.simpleMessage("WebDAV configuration"), - "whitelistMode": MessageLookupByLibrary.simpleMessage("Whitelist mode"), - "years": MessageLookupByLibrary.simpleMessage("Years"), - "zh_CN": MessageLookupByLibrary.simpleMessage("Simplified Chinese") - }; + "about": MessageLookupByLibrary.simpleMessage("About"), + "accessControl": MessageLookupByLibrary.simpleMessage("AccessControl"), + "accessControlAllowDesc": MessageLookupByLibrary.simpleMessage( + "Only allow selected app to enter VPN", + ), + "accessControlDesc": MessageLookupByLibrary.simpleMessage( + "Configure application access proxy", + ), + "accessControlNotAllowDesc": MessageLookupByLibrary.simpleMessage( + "The selected application will be excluded from VPN", + ), + "account": MessageLookupByLibrary.simpleMessage("Account"), + "accountTip": MessageLookupByLibrary.simpleMessage( + "Account cannot be empty", + ), + "action": MessageLookupByLibrary.simpleMessage("Action"), + "action_mode": MessageLookupByLibrary.simpleMessage("Switch mode"), + "action_proxy": MessageLookupByLibrary.simpleMessage("System proxy"), + "action_start": MessageLookupByLibrary.simpleMessage("Start/Stop"), + "action_tun": MessageLookupByLibrary.simpleMessage("TUN"), + "action_view": MessageLookupByLibrary.simpleMessage("Show/Hide"), + "add": MessageLookupByLibrary.simpleMessage("Add"), + "address": MessageLookupByLibrary.simpleMessage("Address"), + "addressHelp": MessageLookupByLibrary.simpleMessage( + "WebDAV server address", + ), + "addressTip": MessageLookupByLibrary.simpleMessage( + "Please enter a valid WebDAV address", + ), + "adminAutoLaunch": MessageLookupByLibrary.simpleMessage( + "Admin auto launch", + ), + "adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage( + "Boot up by using admin mode", + ), + "ago": MessageLookupByLibrary.simpleMessage(" Ago"), + "agree": MessageLookupByLibrary.simpleMessage("Agree"), + "allApps": MessageLookupByLibrary.simpleMessage("All apps"), + "allowBypass": MessageLookupByLibrary.simpleMessage( + "Allow applications to bypass VPN", + ), + "allowBypassDesc": MessageLookupByLibrary.simpleMessage( + "Some apps can bypass VPN when turned on", + ), + "allowLan": MessageLookupByLibrary.simpleMessage("AllowLan"), + "allowLanDesc": MessageLookupByLibrary.simpleMessage( + "Allow access proxy through the LAN", + ), + "app": MessageLookupByLibrary.simpleMessage("App"), + "appAccessControl": MessageLookupByLibrary.simpleMessage( + "App access control", + ), + "appDesc": MessageLookupByLibrary.simpleMessage( + "Processing app related settings", + ), + "application": MessageLookupByLibrary.simpleMessage("Application"), + "applicationDesc": MessageLookupByLibrary.simpleMessage( + "Modify application related settings", + ), + "auto": MessageLookupByLibrary.simpleMessage("Auto"), + "autoCheckUpdate": MessageLookupByLibrary.simpleMessage( + "Auto check updates", + ), + "autoCheckUpdateDesc": MessageLookupByLibrary.simpleMessage( + "Auto check for updates when the app starts", + ), + "autoCloseConnections": MessageLookupByLibrary.simpleMessage( + "Auto lose connections", + ), + "autoCloseConnectionsDesc": MessageLookupByLibrary.simpleMessage( + "Auto close connections after change node", + ), + "autoLaunch": MessageLookupByLibrary.simpleMessage("Auto launch"), + "autoLaunchDesc": MessageLookupByLibrary.simpleMessage( + "Follow the system self startup", + ), + "autoRun": MessageLookupByLibrary.simpleMessage("AutoRun"), + "autoRunDesc": MessageLookupByLibrary.simpleMessage( + "Auto run when the application is opened", + ), + "autoUpdate": MessageLookupByLibrary.simpleMessage("Auto update"), + "autoUpdateInterval": MessageLookupByLibrary.simpleMessage( + "Auto update interval (minutes)", + ), + "backup": MessageLookupByLibrary.simpleMessage("Backup"), + "backupAndRecovery": MessageLookupByLibrary.simpleMessage( + "Backup and Recovery", + ), + "backupAndRecoveryDesc": MessageLookupByLibrary.simpleMessage( + "Sync data via WebDAV or file", + ), + "backupSuccess": MessageLookupByLibrary.simpleMessage("Backup success"), + "bind": MessageLookupByLibrary.simpleMessage("Bind"), + "blacklistMode": MessageLookupByLibrary.simpleMessage("Blacklist mode"), + "bypassDomain": MessageLookupByLibrary.simpleMessage("Bypass domain"), + "bypassDomainDesc": MessageLookupByLibrary.simpleMessage( + "Only takes effect when the system proxy is enabled", + ), + "cacheCorrupt": MessageLookupByLibrary.simpleMessage( + "The cache is corrupt. Do you want to clear it?", + ), + "cancel": MessageLookupByLibrary.simpleMessage("Cancel"), + "cancelFilterSystemApp": MessageLookupByLibrary.simpleMessage( + "Cancel filter system app", + ), + "cancelSelectAll": MessageLookupByLibrary.simpleMessage( + "Cancel select all", + ), + "checkError": MessageLookupByLibrary.simpleMessage("Check error"), + "checkUpdate": MessageLookupByLibrary.simpleMessage("Check for updates"), + "checkUpdateError": MessageLookupByLibrary.simpleMessage( + "The current application is already the latest version", + ), + "checking": MessageLookupByLibrary.simpleMessage("Checking..."), + "clipboardExport": MessageLookupByLibrary.simpleMessage("Export clipboard"), + "clipboardImport": MessageLookupByLibrary.simpleMessage("Clipboard import"), + "columns": MessageLookupByLibrary.simpleMessage("Columns"), + "compatible": MessageLookupByLibrary.simpleMessage("Compatibility mode"), + "compatibleDesc": MessageLookupByLibrary.simpleMessage( + "Opening it will lose part of its application ability and gain the support of full amount of Clash.", + ), + "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), + "connections": MessageLookupByLibrary.simpleMessage("Connections"), + "connectionsDesc": MessageLookupByLibrary.simpleMessage( + "View current connections data", + ), + "connectivity": MessageLookupByLibrary.simpleMessage("Connectivity:"), + "copy": MessageLookupByLibrary.simpleMessage("Copy"), + "copyEnvVar": MessageLookupByLibrary.simpleMessage( + "Copying environment variables", + ), + "copyLink": MessageLookupByLibrary.simpleMessage("Copy link"), + "copySuccess": MessageLookupByLibrary.simpleMessage("Copy success"), + "core": MessageLookupByLibrary.simpleMessage("Core"), + "coreInfo": MessageLookupByLibrary.simpleMessage("Core info"), + "country": MessageLookupByLibrary.simpleMessage("Country"), + "create": MessageLookupByLibrary.simpleMessage("Create"), + "cut": MessageLookupByLibrary.simpleMessage("Cut"), + "dark": MessageLookupByLibrary.simpleMessage("Dark"), + "dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"), + "days": MessageLookupByLibrary.simpleMessage("Days"), + "defaultNameserver": MessageLookupByLibrary.simpleMessage( + "Default nameserver", + ), + "defaultNameserverDesc": MessageLookupByLibrary.simpleMessage( + "For resolving DNS server", + ), + "defaultSort": MessageLookupByLibrary.simpleMessage("Sort by default"), + "defaultText": MessageLookupByLibrary.simpleMessage("Default"), + "delay": MessageLookupByLibrary.simpleMessage("Delay"), + "delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"), + "delete": MessageLookupByLibrary.simpleMessage("Delete"), + "deleteProfileTip": MessageLookupByLibrary.simpleMessage( + "Sure you want to delete the current profile?", + ), + "desc": MessageLookupByLibrary.simpleMessage( + "A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.", + ), + "direct": MessageLookupByLibrary.simpleMessage("Direct"), + "disclaimer": MessageLookupByLibrary.simpleMessage("Disclaimer"), + "disclaimerDesc": MessageLookupByLibrary.simpleMessage( + "This software is only used for non-commercial purposes such as learning exchanges and scientific research. It is strictly prohibited to use this software for commercial purposes. Any commercial activity, if any, has nothing to do with this software.", + ), + "discoverNewVersion": MessageLookupByLibrary.simpleMessage( + "Discover the new version", + ), + "discovery": MessageLookupByLibrary.simpleMessage( + "Discovery a new version", + ), + "dnsDesc": MessageLookupByLibrary.simpleMessage( + "Update DNS related settings", + ), + "dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"), + "doYouWantToPass": MessageLookupByLibrary.simpleMessage( + "Do you want to pass", + ), + "domain": MessageLookupByLibrary.simpleMessage("Domain"), + "download": MessageLookupByLibrary.simpleMessage("Download"), + "edit": MessageLookupByLibrary.simpleMessage("Edit"), + "en": MessageLookupByLibrary.simpleMessage("English"), + "entries": MessageLookupByLibrary.simpleMessage(" entries"), + "exclude": MessageLookupByLibrary.simpleMessage("Hidden from recent tasks"), + "excludeDesc": MessageLookupByLibrary.simpleMessage( + "When the app is in the background, the app is hidden from the recent task", + ), + "exit": MessageLookupByLibrary.simpleMessage("Exit"), + "expand": MessageLookupByLibrary.simpleMessage("Standard"), + "expirationTime": MessageLookupByLibrary.simpleMessage("Expiration time"), + "exportFile": MessageLookupByLibrary.simpleMessage("Export file"), + "exportLogs": MessageLookupByLibrary.simpleMessage("Export logs"), + "exportSuccess": MessageLookupByLibrary.simpleMessage("Export Success"), + "externalController": MessageLookupByLibrary.simpleMessage( + "ExternalController", + ), + "externalControllerDesc": MessageLookupByLibrary.simpleMessage( + "Once enabled, the Clash kernel can be controlled on port 9090", + ), + "externalLink": MessageLookupByLibrary.simpleMessage("External link"), + "externalResources": MessageLookupByLibrary.simpleMessage( + "External resources", + ), + "fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip filter"), + "fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip range"), + "fallback": MessageLookupByLibrary.simpleMessage("Fallback"), + "fallbackDesc": MessageLookupByLibrary.simpleMessage( + "Generally use offshore DNS", + ), + "fallbackFilter": MessageLookupByLibrary.simpleMessage("Fallback filter"), + "file": MessageLookupByLibrary.simpleMessage("File"), + "fileDesc": MessageLookupByLibrary.simpleMessage("Directly upload profile"), + "fileIsUpdate": MessageLookupByLibrary.simpleMessage( + "The file has been modified. Do you want to save the changes?", + ), + "filterSystemApp": MessageLookupByLibrary.simpleMessage( + "Filter system app", + ), + "findProcessMode": MessageLookupByLibrary.simpleMessage("Find process"), + "findProcessModeDesc": MessageLookupByLibrary.simpleMessage( + "There is a risk of flashback after opening", + ), + "fontFamily": MessageLookupByLibrary.simpleMessage("FontFamily"), + "fourColumns": MessageLookupByLibrary.simpleMessage("Four columns"), + "general": MessageLookupByLibrary.simpleMessage("General"), + "generalDesc": MessageLookupByLibrary.simpleMessage( + "Overwrite general settings", + ), + "geoData": MessageLookupByLibrary.simpleMessage("GeoData"), + "geodataLoader": MessageLookupByLibrary.simpleMessage( + "Geo Low Memory Mode", + ), + "geodataLoaderDesc": MessageLookupByLibrary.simpleMessage( + "Enabling will use the Geo low memory loader", + ), + "geoipCode": MessageLookupByLibrary.simpleMessage("Geoip code"), + "global": MessageLookupByLibrary.simpleMessage("Global"), + "go": MessageLookupByLibrary.simpleMessage("Go"), + "goDownload": MessageLookupByLibrary.simpleMessage("Go to download"), + "hasCacheChange": MessageLookupByLibrary.simpleMessage( + "Do you want to cache the changes?", + ), + "hostsDesc": MessageLookupByLibrary.simpleMessage("Add Hosts"), + "hotkeyConflict": MessageLookupByLibrary.simpleMessage("Hotkey conflict"), + "hotkeyManagement": MessageLookupByLibrary.simpleMessage( + "Hotkey Management", + ), + "hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage( + "Use keyboard to control applications", + ), + "hours": MessageLookupByLibrary.simpleMessage("Hours"), + "icon": MessageLookupByLibrary.simpleMessage("Icon"), + "iconConfiguration": MessageLookupByLibrary.simpleMessage( + "Icon configuration", + ), + "iconStyle": MessageLookupByLibrary.simpleMessage("Icon style"), + "importFromURL": MessageLookupByLibrary.simpleMessage("Import from URL"), + "infiniteTime": MessageLookupByLibrary.simpleMessage("Long term effective"), + "init": MessageLookupByLibrary.simpleMessage("Init"), + "inputCorrectHotkey": MessageLookupByLibrary.simpleMessage( + "Please enter the correct hotkey", + ), + "intelligentSelected": MessageLookupByLibrary.simpleMessage( + "Intelligent selection", + ), + "intranetIP": MessageLookupByLibrary.simpleMessage("Intranet IP"), + "ipcidr": MessageLookupByLibrary.simpleMessage("Ipcidr"), + "ipv6Desc": MessageLookupByLibrary.simpleMessage( + "When turned on it will be able to receive IPv6 traffic", + ), + "ipv6InboundDesc": MessageLookupByLibrary.simpleMessage( + "Allow IPv6 inbound", + ), + "just": MessageLookupByLibrary.simpleMessage("Just"), + "keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage( + "Tcp keep alive interval", + ), + "key": MessageLookupByLibrary.simpleMessage("Key"), + "language": MessageLookupByLibrary.simpleMessage("Language"), + "layout": MessageLookupByLibrary.simpleMessage("Layout"), + "light": MessageLookupByLibrary.simpleMessage("Light"), + "list": MessageLookupByLibrary.simpleMessage("List"), + "local": MessageLookupByLibrary.simpleMessage("Local"), + "localBackupDesc": MessageLookupByLibrary.simpleMessage( + "Backup local data to local", + ), + "localRecoveryDesc": MessageLookupByLibrary.simpleMessage( + "Recovery data from file", + ), + "logLevel": MessageLookupByLibrary.simpleMessage("LogLevel"), + "logcat": MessageLookupByLibrary.simpleMessage("Logcat"), + "logcatDesc": MessageLookupByLibrary.simpleMessage( + "Disabling will hide the log entry", + ), + "logs": MessageLookupByLibrary.simpleMessage("Logs"), + "logsDesc": MessageLookupByLibrary.simpleMessage("Log capture records"), + "loopback": MessageLookupByLibrary.simpleMessage("Loopback unlock tool"), + "loopbackDesc": MessageLookupByLibrary.simpleMessage( + "Used for UWP loopback unlocking", + ), + "loose": MessageLookupByLibrary.simpleMessage("Loose"), + "memoryInfo": MessageLookupByLibrary.simpleMessage("Memory info"), + "min": MessageLookupByLibrary.simpleMessage("Min"), + "minimizeOnExit": MessageLookupByLibrary.simpleMessage("Minimize on exit"), + "minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage( + "Modify the default system exit event", + ), + "minutes": MessageLookupByLibrary.simpleMessage("Minutes"), + "mode": MessageLookupByLibrary.simpleMessage("Mode"), + "months": MessageLookupByLibrary.simpleMessage("Months"), + "more": MessageLookupByLibrary.simpleMessage("More"), + "name": MessageLookupByLibrary.simpleMessage("Name"), + "nameSort": MessageLookupByLibrary.simpleMessage("Sort by name"), + "nameserver": MessageLookupByLibrary.simpleMessage("Nameserver"), + "nameserverDesc": MessageLookupByLibrary.simpleMessage( + "For resolving domain", + ), + "nameserverPolicy": MessageLookupByLibrary.simpleMessage( + "Nameserver policy", + ), + "nameserverPolicyDesc": MessageLookupByLibrary.simpleMessage( + "Specify the corresponding nameserver policy", + ), + "network": MessageLookupByLibrary.simpleMessage("Network"), + "networkDesc": MessageLookupByLibrary.simpleMessage( + "Modify network-related settings", + ), + "networkDetection": MessageLookupByLibrary.simpleMessage( + "Network detection", + ), + "networkSpeed": MessageLookupByLibrary.simpleMessage("Network speed"), + "noData": MessageLookupByLibrary.simpleMessage("No data"), + "noHotKey": MessageLookupByLibrary.simpleMessage("No HotKey"), + "noIcon": MessageLookupByLibrary.simpleMessage("None"), + "noInfo": MessageLookupByLibrary.simpleMessage("No info"), + "noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("No more info"), + "noNetwork": MessageLookupByLibrary.simpleMessage("No network"), + "noProxy": MessageLookupByLibrary.simpleMessage("No proxy"), + "noProxyDesc": MessageLookupByLibrary.simpleMessage( + "Please create a profile or add a valid profile", + ), + "notEmpty": MessageLookupByLibrary.simpleMessage("Cannot be empty"), + "notSelectedTip": MessageLookupByLibrary.simpleMessage( + "The current proxy group cannot be selected.", + ), + "nullConnectionsDesc": MessageLookupByLibrary.simpleMessage( + "No connections", + ), + "nullCoreInfoDesc": MessageLookupByLibrary.simpleMessage( + "Unable to obtain core info", + ), + "nullLogsDesc": MessageLookupByLibrary.simpleMessage("No logs"), + "nullProfileDesc": MessageLookupByLibrary.simpleMessage( + "No profile, Please add a profile", + ), + "nullProxies": MessageLookupByLibrary.simpleMessage("No proxies"), + "nullRequestsDesc": MessageLookupByLibrary.simpleMessage("No requests"), + "oneColumn": MessageLookupByLibrary.simpleMessage("One column"), + "onlyIcon": MessageLookupByLibrary.simpleMessage("Icon"), + "onlyOtherApps": MessageLookupByLibrary.simpleMessage( + "Only third-party apps", + ), + "onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage( + "Only statistics proxy", + ), + "onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage( + "When turned on, only statistics proxy traffic", + ), + "options": MessageLookupByLibrary.simpleMessage("Options"), + "other": MessageLookupByLibrary.simpleMessage("Other"), + "otherContributors": MessageLookupByLibrary.simpleMessage( + "Other contributors", + ), + "outboundMode": MessageLookupByLibrary.simpleMessage("Outbound mode"), + "override": MessageLookupByLibrary.simpleMessage("Override"), + "overrideDesc": MessageLookupByLibrary.simpleMessage( + "Override Proxy related config", + ), + "overrideDns": MessageLookupByLibrary.simpleMessage("Override Dns"), + "overrideDnsDesc": MessageLookupByLibrary.simpleMessage( + "Turning it on will override the DNS options in the profile", + ), + "password": MessageLookupByLibrary.simpleMessage("Password"), + "passwordTip": MessageLookupByLibrary.simpleMessage( + "Password cannot be empty", + ), + "paste": MessageLookupByLibrary.simpleMessage("Paste"), + "pleaseBindWebDAV": MessageLookupByLibrary.simpleMessage( + "Please bind WebDAV", + ), + "pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage( + "Please enter the admin password", + ), + "pleaseUploadFile": MessageLookupByLibrary.simpleMessage( + "Please upload file", + ), + "pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage( + "Please upload a valid QR code", + ), + "port": MessageLookupByLibrary.simpleMessage("Port"), + "preferH3Desc": MessageLookupByLibrary.simpleMessage( + "Prioritize the use of DOH\'s http/3", + ), + "pressKeyboard": MessageLookupByLibrary.simpleMessage( + "Please press the keyboard.", + ), + "preview": MessageLookupByLibrary.simpleMessage("Preview"), + "profile": MessageLookupByLibrary.simpleMessage("Profile"), + "profileAutoUpdateIntervalInvalidValidationDesc": + MessageLookupByLibrary.simpleMessage( + "Please input a valid interval time format", + ), + "profileAutoUpdateIntervalNullValidationDesc": + MessageLookupByLibrary.simpleMessage( + "Please enter the auto update interval time", + ), + "profileHasUpdate": MessageLookupByLibrary.simpleMessage( + "The profile has been modified. Do you want to disable auto update?", + ), + "profileNameNullValidationDesc": MessageLookupByLibrary.simpleMessage( + "Please input the profile name", + ), + "profileParseErrorDesc": MessageLookupByLibrary.simpleMessage( + "profile parse error", + ), + "profileUrlInvalidValidationDesc": MessageLookupByLibrary.simpleMessage( + "Please input a valid profile URL", + ), + "profileUrlNullValidationDesc": MessageLookupByLibrary.simpleMessage( + "Please input the profile URL", + ), + "profiles": MessageLookupByLibrary.simpleMessage("Profiles"), + "profilesSort": MessageLookupByLibrary.simpleMessage("Profiles sort"), + "project": MessageLookupByLibrary.simpleMessage("Project"), + "providers": MessageLookupByLibrary.simpleMessage("Providers"), + "proxies": MessageLookupByLibrary.simpleMessage("Proxies"), + "proxiesSetting": MessageLookupByLibrary.simpleMessage("Proxies setting"), + "proxyGroup": MessageLookupByLibrary.simpleMessage("Proxy group"), + "proxyNameserver": MessageLookupByLibrary.simpleMessage("Proxy nameserver"), + "proxyNameserverDesc": MessageLookupByLibrary.simpleMessage( + "Domain for resolving proxy nodes", + ), + "proxyPort": MessageLookupByLibrary.simpleMessage("ProxyPort"), + "proxyPortDesc": MessageLookupByLibrary.simpleMessage( + "Set the Clash listening port", + ), + "proxyProviders": MessageLookupByLibrary.simpleMessage("Proxy providers"), + "prueBlackMode": MessageLookupByLibrary.simpleMessage("Prue black mode"), + "qrcode": MessageLookupByLibrary.simpleMessage("QR code"), + "qrcodeDesc": MessageLookupByLibrary.simpleMessage( + "Scan QR code to obtain profile", + ), + "recovery": MessageLookupByLibrary.simpleMessage("Recovery"), + "recoveryAll": MessageLookupByLibrary.simpleMessage("Recovery all data"), + "recoveryProfiles": MessageLookupByLibrary.simpleMessage( + "Only recovery profiles", + ), + "recoverySuccess": MessageLookupByLibrary.simpleMessage("Recovery success"), + "regExp": MessageLookupByLibrary.simpleMessage("RegExp"), + "remote": MessageLookupByLibrary.simpleMessage("Remote"), + "remoteBackupDesc": MessageLookupByLibrary.simpleMessage( + "Backup local data to WebDAV", + ), + "remoteRecoveryDesc": MessageLookupByLibrary.simpleMessage( + "Recovery data from WebDAV", + ), + "remove": MessageLookupByLibrary.simpleMessage("Remove"), + "requests": MessageLookupByLibrary.simpleMessage("Requests"), + "requestsDesc": MessageLookupByLibrary.simpleMessage( + "View recently request records", + ), + "reset": MessageLookupByLibrary.simpleMessage("Reset"), + "resetTip": MessageLookupByLibrary.simpleMessage("Make sure to reset"), + "resources": MessageLookupByLibrary.simpleMessage("Resources"), + "resourcesDesc": MessageLookupByLibrary.simpleMessage( + "External resource related info", + ), + "respectRules": MessageLookupByLibrary.simpleMessage("Respect rules"), + "respectRulesDesc": MessageLookupByLibrary.simpleMessage( + "DNS connection following rules, need to configure proxy-server-nameserver", + ), + "routeAddress": MessageLookupByLibrary.simpleMessage("Route address"), + "routeAddressDesc": MessageLookupByLibrary.simpleMessage( + "Config listen route address", + ), + "routeMode": MessageLookupByLibrary.simpleMessage("Route mode"), + "routeMode_bypassPrivate": MessageLookupByLibrary.simpleMessage( + "Bypass private route address", + ), + "routeMode_config": MessageLookupByLibrary.simpleMessage("Use config"), + "rule": MessageLookupByLibrary.simpleMessage("Rule"), + "ruleProviders": MessageLookupByLibrary.simpleMessage("Rule providers"), + "save": MessageLookupByLibrary.simpleMessage("Save"), + "search": MessageLookupByLibrary.simpleMessage("Search"), + "seconds": MessageLookupByLibrary.simpleMessage("Seconds"), + "selectAll": MessageLookupByLibrary.simpleMessage("Select all"), + "selected": MessageLookupByLibrary.simpleMessage("Selected"), + "settings": MessageLookupByLibrary.simpleMessage("Settings"), + "show": MessageLookupByLibrary.simpleMessage("Show"), + "shrink": MessageLookupByLibrary.simpleMessage("Shrink"), + "silentLaunch": MessageLookupByLibrary.simpleMessage("SilentLaunch"), + "silentLaunchDesc": MessageLookupByLibrary.simpleMessage( + "Start in the background", + ), + "size": MessageLookupByLibrary.simpleMessage("Size"), + "sort": MessageLookupByLibrary.simpleMessage("Sort"), + "source": MessageLookupByLibrary.simpleMessage("Source"), + "stackMode": MessageLookupByLibrary.simpleMessage("Stack mode"), + "standard": MessageLookupByLibrary.simpleMessage("Standard"), + "start": MessageLookupByLibrary.simpleMessage("Start"), + "startVpn": MessageLookupByLibrary.simpleMessage("Starting VPN..."), + "status": MessageLookupByLibrary.simpleMessage("Status"), + "statusDesc": MessageLookupByLibrary.simpleMessage( + "System DNS will be used when turned off", + ), + "stop": MessageLookupByLibrary.simpleMessage("Stop"), + "stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."), + "style": MessageLookupByLibrary.simpleMessage("Style"), + "submit": MessageLookupByLibrary.simpleMessage("Submit"), + "sync": MessageLookupByLibrary.simpleMessage("Sync"), + "system": MessageLookupByLibrary.simpleMessage("System"), + "systemFont": MessageLookupByLibrary.simpleMessage("System font"), + "systemProxy": MessageLookupByLibrary.simpleMessage("System proxy"), + "systemProxyDesc": MessageLookupByLibrary.simpleMessage( + "Attach HTTP proxy to VpnService", + ), + "tab": MessageLookupByLibrary.simpleMessage("Tab"), + "tabAnimation": MessageLookupByLibrary.simpleMessage("Tab animation"), + "tabAnimationDesc": MessageLookupByLibrary.simpleMessage( + "When enabled, the home tab will add a toggle animation", + ), + "tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP concurrent"), + "tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage( + "Enabling it will allow TCP concurrency", + ), + "testUrl": MessageLookupByLibrary.simpleMessage("Test url"), + "theme": MessageLookupByLibrary.simpleMessage("Theme"), + "themeColor": MessageLookupByLibrary.simpleMessage("Theme color"), + "themeDesc": MessageLookupByLibrary.simpleMessage( + "Set dark mode,adjust the color", + ), + "themeMode": MessageLookupByLibrary.simpleMessage("Theme mode"), + "threeColumns": MessageLookupByLibrary.simpleMessage("Three columns"), + "tight": MessageLookupByLibrary.simpleMessage("Tight"), + "time": MessageLookupByLibrary.simpleMessage("Time"), + "tip": MessageLookupByLibrary.simpleMessage("tip"), + "toggle": MessageLookupByLibrary.simpleMessage("Toggle"), + "tools": MessageLookupByLibrary.simpleMessage("Tools"), + "trafficUsage": MessageLookupByLibrary.simpleMessage("Traffic usage"), + "tun": MessageLookupByLibrary.simpleMessage("TUN"), + "tunDesc": MessageLookupByLibrary.simpleMessage( + "only effective in administrator mode", + ), + "twoColumns": MessageLookupByLibrary.simpleMessage("Two columns"), + "unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage( + "unable to update current profile", + ), + "unifiedDelay": MessageLookupByLibrary.simpleMessage("Unified delay"), + "unifiedDelayDesc": MessageLookupByLibrary.simpleMessage( + "Remove extra delays such as handshaking", + ), + "unknown": MessageLookupByLibrary.simpleMessage("Unknown"), + "update": MessageLookupByLibrary.simpleMessage("Update"), + "upload": MessageLookupByLibrary.simpleMessage("Upload"), + "url": MessageLookupByLibrary.simpleMessage("URL"), + "urlDesc": MessageLookupByLibrary.simpleMessage( + "Obtain profile through URL", + ), + "useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"), + "useSystemHosts": MessageLookupByLibrary.simpleMessage("Use system hosts"), + "value": MessageLookupByLibrary.simpleMessage("Value"), + "view": MessageLookupByLibrary.simpleMessage("View"), + "vpnDesc": MessageLookupByLibrary.simpleMessage( + "Modify VPN related settings", + ), + "vpnEnableDesc": MessageLookupByLibrary.simpleMessage( + "Auto routes all system traffic through VpnService", + ), + "vpnSystemProxyDesc": MessageLookupByLibrary.simpleMessage( + "Attach HTTP proxy to VpnService", + ), + "vpnTip": MessageLookupByLibrary.simpleMessage( + "Changes take effect after restarting the VPN", + ), + "webDAVConfiguration": MessageLookupByLibrary.simpleMessage( + "WebDAV configuration", + ), + "whitelistMode": MessageLookupByLibrary.simpleMessage("Whitelist mode"), + "years": MessageLookupByLibrary.simpleMessage("Years"), + "zh_CN": MessageLookupByLibrary.simpleMessage("Simplified Chinese"), + }; } diff --git a/lib/l10n/intl/messages_zh_CN.dart b/lib/l10n/intl/messages_zh_CN.dart index e145047d..8127657a 100644 --- a/lib/l10n/intl/messages_zh_CN.dart +++ b/lib/l10n/intl/messages_zh_CN.dart @@ -22,390 +22,391 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "about": MessageLookupByLibrary.simpleMessage("关于"), - "accessControl": MessageLookupByLibrary.simpleMessage("访问控制"), - "accessControlAllowDesc": - MessageLookupByLibrary.simpleMessage("只允许选中应用进入VPN"), - "accessControlDesc": MessageLookupByLibrary.simpleMessage("配置应用访问代理"), - "accessControlNotAllowDesc": - MessageLookupByLibrary.simpleMessage("选中应用将会被排除在VPN之外"), - "account": MessageLookupByLibrary.simpleMessage("账号"), - "accountTip": MessageLookupByLibrary.simpleMessage("账号不能为空"), - "action": MessageLookupByLibrary.simpleMessage("操作"), - "action_mode": MessageLookupByLibrary.simpleMessage("切换模式"), - "action_proxy": MessageLookupByLibrary.simpleMessage("系统代理"), - "action_start": MessageLookupByLibrary.simpleMessage("启动/停止"), - "action_tun": MessageLookupByLibrary.simpleMessage("虚拟网卡"), - "action_view": MessageLookupByLibrary.simpleMessage("显示/隐藏"), - "add": MessageLookupByLibrary.simpleMessage("添加"), - "address": MessageLookupByLibrary.simpleMessage("地址"), - "addressHelp": MessageLookupByLibrary.simpleMessage("WebDAV服务器地址"), - "addressTip": MessageLookupByLibrary.simpleMessage("请输入有效的WebDAV地址"), - "adminAutoLaunch": MessageLookupByLibrary.simpleMessage("管理员自启动"), - "adminAutoLaunchDesc": - MessageLookupByLibrary.simpleMessage("使用管理员模式开机自启动"), - "ago": MessageLookupByLibrary.simpleMessage("前"), - "agree": MessageLookupByLibrary.simpleMessage("同意"), - "allApps": MessageLookupByLibrary.simpleMessage("所有应用"), - "allowBypass": MessageLookupByLibrary.simpleMessage("允许应用绕过VPN"), - "allowBypassDesc": - MessageLookupByLibrary.simpleMessage("开启后部分应用可绕过VPN"), - "allowLan": MessageLookupByLibrary.simpleMessage("局域网代理"), - "allowLanDesc": MessageLookupByLibrary.simpleMessage("允许通过局域网访问代理"), - "app": MessageLookupByLibrary.simpleMessage("应用"), - "appAccessControl": MessageLookupByLibrary.simpleMessage("应用访问控制"), - "appDesc": MessageLookupByLibrary.simpleMessage("处理应用相关设置"), - "application": MessageLookupByLibrary.simpleMessage("应用程序"), - "applicationDesc": MessageLookupByLibrary.simpleMessage("修改应用程序相关设置"), - "auto": MessageLookupByLibrary.simpleMessage("自动"), - "autoCheckUpdate": MessageLookupByLibrary.simpleMessage("自动检查更新"), - "autoCheckUpdateDesc": - MessageLookupByLibrary.simpleMessage("应用启动时自动检查更新"), - "autoCloseConnections": MessageLookupByLibrary.simpleMessage("自动关闭连接"), - "autoCloseConnectionsDesc": - MessageLookupByLibrary.simpleMessage("切换节点后自动关闭连接"), - "autoLaunch": MessageLookupByLibrary.simpleMessage("自启动"), - "autoLaunchDesc": MessageLookupByLibrary.simpleMessage("跟随系统自启动"), - "autoRun": MessageLookupByLibrary.simpleMessage("自动运行"), - "autoRunDesc": MessageLookupByLibrary.simpleMessage("应用打开时自动运行"), - "autoUpdate": MessageLookupByLibrary.simpleMessage("自动更新"), - "autoUpdateInterval": - MessageLookupByLibrary.simpleMessage("自动更新间隔(分钟)"), - "backup": MessageLookupByLibrary.simpleMessage("备份"), - "backupAndRecovery": MessageLookupByLibrary.simpleMessage("备份与恢复"), - "backupAndRecoveryDesc": - MessageLookupByLibrary.simpleMessage("通过WebDAV或者文件同步数据"), - "backupSuccess": MessageLookupByLibrary.simpleMessage("备份成功"), - "bind": MessageLookupByLibrary.simpleMessage("绑定"), - "blacklistMode": MessageLookupByLibrary.simpleMessage("黑名单模式"), - "bypassDomain": MessageLookupByLibrary.simpleMessage("排除域名"), - "bypassDomainDesc": MessageLookupByLibrary.simpleMessage("仅在系统代理启用时生效"), - "cancel": MessageLookupByLibrary.simpleMessage("取消"), - "cancelFilterSystemApp": - MessageLookupByLibrary.simpleMessage("取消过滤系统应用"), - "cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"), - "checkError": MessageLookupByLibrary.simpleMessage("检测失败"), - "checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"), - "checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"), - "checking": MessageLookupByLibrary.simpleMessage("检测中..."), - "clipboardExport": MessageLookupByLibrary.simpleMessage("导出剪贴板"), - "clipboardImport": MessageLookupByLibrary.simpleMessage("剪贴板导入"), - "columns": MessageLookupByLibrary.simpleMessage("列数"), - "compatible": MessageLookupByLibrary.simpleMessage("兼容模式"), - "compatibleDesc": - MessageLookupByLibrary.simpleMessage("开启将失去部分应用能力,获得全量的Clash的支持"), - "confirm": MessageLookupByLibrary.simpleMessage("确定"), - "connections": MessageLookupByLibrary.simpleMessage("连接"), - "connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"), - "connectivity": MessageLookupByLibrary.simpleMessage("连通性:"), - "copy": MessageLookupByLibrary.simpleMessage("复制"), - "copyEnvVar": MessageLookupByLibrary.simpleMessage("复制环境变量"), - "copyLink": MessageLookupByLibrary.simpleMessage("复制链接"), - "copySuccess": MessageLookupByLibrary.simpleMessage("复制成功"), - "core": MessageLookupByLibrary.simpleMessage("内核"), - "coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"), - "country": MessageLookupByLibrary.simpleMessage("区域"), - "create": MessageLookupByLibrary.simpleMessage("创建"), - "cut": MessageLookupByLibrary.simpleMessage("剪切"), - "dark": MessageLookupByLibrary.simpleMessage("深色"), - "dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"), - "days": MessageLookupByLibrary.simpleMessage("天"), - "defaultNameserver": MessageLookupByLibrary.simpleMessage("默认域名服务器"), - "defaultNameserverDesc": - MessageLookupByLibrary.simpleMessage("用于解析DNS服务器"), - "defaultSort": MessageLookupByLibrary.simpleMessage("按默认排序"), - "defaultText": MessageLookupByLibrary.simpleMessage("默认"), - "delay": MessageLookupByLibrary.simpleMessage("延迟"), - "delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"), - "delete": MessageLookupByLibrary.simpleMessage("删除"), - "deleteProfileTip": MessageLookupByLibrary.simpleMessage("确定要删除当前配置吗?"), - "desc": MessageLookupByLibrary.simpleMessage( - "基于ClashMeta的多平台代理客户端,简单易用,开源无广告。"), - "direct": MessageLookupByLibrary.simpleMessage("直连"), - "disclaimer": MessageLookupByLibrary.simpleMessage("免责声明"), - "disclaimerDesc": MessageLookupByLibrary.simpleMessage( - "本软件仅供学习交流、科研等非商业性质的用途,严禁将本软件用于商业目的。如有任何商业行为,均与本软件无关。"), - "discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"), - "discovery": MessageLookupByLibrary.simpleMessage("发现新版本"), - "dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"), - "dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"), - "doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"), - "domain": MessageLookupByLibrary.simpleMessage("域名"), - "download": MessageLookupByLibrary.simpleMessage("下载"), - "edit": MessageLookupByLibrary.simpleMessage("编辑"), - "en": MessageLookupByLibrary.simpleMessage("英语"), - "entries": MessageLookupByLibrary.simpleMessage("个条目"), - "exclude": MessageLookupByLibrary.simpleMessage("从最近任务中隐藏"), - "excludeDesc": - MessageLookupByLibrary.simpleMessage("应用在后台时,从最近任务中隐藏应用"), - "exit": MessageLookupByLibrary.simpleMessage("退出"), - "expand": MessageLookupByLibrary.simpleMessage("标准"), - "expirationTime": MessageLookupByLibrary.simpleMessage("到期时间"), - "exportFile": MessageLookupByLibrary.simpleMessage("导出文件"), - "exportLogs": MessageLookupByLibrary.simpleMessage("导出日志"), - "exportSuccess": MessageLookupByLibrary.simpleMessage("导出成功"), - "externalController": MessageLookupByLibrary.simpleMessage("外部控制器"), - "externalControllerDesc": - MessageLookupByLibrary.simpleMessage("开启后将可以通过9090端口控制Clash内核"), - "externalLink": MessageLookupByLibrary.simpleMessage("外部链接"), - "externalResources": MessageLookupByLibrary.simpleMessage("外部资源"), - "fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip过滤"), - "fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip范围"), - "fallback": MessageLookupByLibrary.simpleMessage("Fallback"), - "fallbackDesc": MessageLookupByLibrary.simpleMessage("一般情况下使用境外DNS"), - "fallbackFilter": MessageLookupByLibrary.simpleMessage("Fallback过滤"), - "file": MessageLookupByLibrary.simpleMessage("文件"), - "fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"), - "fileIsUpdate": MessageLookupByLibrary.simpleMessage("文件有修改,是否保存修改"), - "filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"), - "findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"), - "findProcessModeDesc": - MessageLookupByLibrary.simpleMessage("开启后存在闪退风险"), - "fontFamily": MessageLookupByLibrary.simpleMessage("字体"), - "fourColumns": MessageLookupByLibrary.simpleMessage("四列"), - "general": MessageLookupByLibrary.simpleMessage("基础"), - "generalDesc": MessageLookupByLibrary.simpleMessage("覆写基础设置"), - "geoData": MessageLookupByLibrary.simpleMessage("地理数据"), - "geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"), - "geodataLoaderDesc": - MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"), - "geoipCode": MessageLookupByLibrary.simpleMessage("Geoip代码"), - "global": MessageLookupByLibrary.simpleMessage("全局"), - "go": MessageLookupByLibrary.simpleMessage("前往"), - "goDownload": MessageLookupByLibrary.simpleMessage("前往下载"), - "hasCacheChange": MessageLookupByLibrary.simpleMessage("是否缓存修改"), - "hostsDesc": MessageLookupByLibrary.simpleMessage("追加Hosts"), - "hotkeyConflict": MessageLookupByLibrary.simpleMessage("快捷键冲突"), - "hotkeyManagement": MessageLookupByLibrary.simpleMessage("快捷键管理"), - "hotkeyManagementDesc": - MessageLookupByLibrary.simpleMessage("使用键盘控制应用程序"), - "hours": MessageLookupByLibrary.simpleMessage("小时"), - "icon": MessageLookupByLibrary.simpleMessage("图片"), - "iconConfiguration": MessageLookupByLibrary.simpleMessage("图片配置"), - "iconStyle": MessageLookupByLibrary.simpleMessage("图标样式"), - "importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"), - "infiniteTime": MessageLookupByLibrary.simpleMessage("长期有效"), - "init": MessageLookupByLibrary.simpleMessage("初始化"), - "inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("请输入正确的快捷键"), - "intelligentSelected": MessageLookupByLibrary.simpleMessage("智能选择"), - "intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"), - "ipcidr": MessageLookupByLibrary.simpleMessage("IP/掩码"), - "ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"), - "ipv6InboundDesc": MessageLookupByLibrary.simpleMessage("允许IPv6入站"), - "just": MessageLookupByLibrary.simpleMessage("刚刚"), - "keepAliveIntervalDesc": - MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"), - "key": MessageLookupByLibrary.simpleMessage("键"), - "language": MessageLookupByLibrary.simpleMessage("语言"), - "layout": MessageLookupByLibrary.simpleMessage("布局"), - "light": MessageLookupByLibrary.simpleMessage("浅色"), - "list": MessageLookupByLibrary.simpleMessage("列表"), - "local": MessageLookupByLibrary.simpleMessage("本地"), - "localBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到本地"), - "localRecoveryDesc": MessageLookupByLibrary.simpleMessage("通过文件恢复数据"), - "logLevel": MessageLookupByLibrary.simpleMessage("日志等级"), - "logcat": MessageLookupByLibrary.simpleMessage("日志捕获"), - "logcatDesc": MessageLookupByLibrary.simpleMessage("禁用将会隐藏日志入口"), - "logs": MessageLookupByLibrary.simpleMessage("日志"), - "logsDesc": MessageLookupByLibrary.simpleMessage("日志捕获记录"), - "loopback": MessageLookupByLibrary.simpleMessage("回环解锁工具"), - "loopbackDesc": MessageLookupByLibrary.simpleMessage("用于UWP回环解锁"), - "loose": MessageLookupByLibrary.simpleMessage("宽松"), - "memoryInfo": MessageLookupByLibrary.simpleMessage("内存信息"), - "min": MessageLookupByLibrary.simpleMessage("最小"), - "minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"), - "minimizeOnExitDesc": - MessageLookupByLibrary.simpleMessage("修改系统默认退出事件"), - "minutes": MessageLookupByLibrary.simpleMessage("分钟"), - "mode": MessageLookupByLibrary.simpleMessage("模式"), - "months": MessageLookupByLibrary.simpleMessage("月"), - "more": MessageLookupByLibrary.simpleMessage("更多"), - "name": MessageLookupByLibrary.simpleMessage("名称"), - "nameSort": MessageLookupByLibrary.simpleMessage("按名称排序"), - "nameserver": MessageLookupByLibrary.simpleMessage("域名服务器"), - "nameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析域名"), - "nameserverPolicy": MessageLookupByLibrary.simpleMessage("域名服务器策略"), - "nameserverPolicyDesc": - MessageLookupByLibrary.simpleMessage("指定对应域名服务器策略"), - "network": MessageLookupByLibrary.simpleMessage("网络"), - "networkDesc": MessageLookupByLibrary.simpleMessage("修改网络相关设置"), - "networkDetection": MessageLookupByLibrary.simpleMessage("网络检测"), - "networkSpeed": MessageLookupByLibrary.simpleMessage("网络速度"), - "noData": MessageLookupByLibrary.simpleMessage("暂无数据"), - "noHotKey": MessageLookupByLibrary.simpleMessage("暂无快捷键"), - "noIcon": MessageLookupByLibrary.simpleMessage("无图标"), - "noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"), - "noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("暂无更多信息"), - "noNetwork": MessageLookupByLibrary.simpleMessage("无网络"), - "noProxy": MessageLookupByLibrary.simpleMessage("暂无代理"), - "noProxyDesc": - MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"), - "notEmpty": MessageLookupByLibrary.simpleMessage("不能为空"), - "notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"), - "nullConnectionsDesc": MessageLookupByLibrary.simpleMessage("暂无连接"), - "nullCoreInfoDesc": MessageLookupByLibrary.simpleMessage("无法获取内核信息"), - "nullLogsDesc": MessageLookupByLibrary.simpleMessage("暂无日志"), - "nullProfileDesc": - MessageLookupByLibrary.simpleMessage("没有配置文件,请先添加配置文件"), - "nullProxies": MessageLookupByLibrary.simpleMessage("暂无代理"), - "nullRequestsDesc": MessageLookupByLibrary.simpleMessage("暂无请求"), - "oneColumn": MessageLookupByLibrary.simpleMessage("一列"), - "onlyIcon": MessageLookupByLibrary.simpleMessage("仅图标"), - "onlyOtherApps": MessageLookupByLibrary.simpleMessage("仅第三方应用"), - "onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage("仅统计代理"), - "onlyStatisticsProxyDesc": - MessageLookupByLibrary.simpleMessage("开启后,将只统计代理流量"), - "options": MessageLookupByLibrary.simpleMessage("选项"), - "other": MessageLookupByLibrary.simpleMessage("其他"), - "otherContributors": MessageLookupByLibrary.simpleMessage("其他贡献者"), - "outboundMode": MessageLookupByLibrary.simpleMessage("出站模式"), - "override": MessageLookupByLibrary.simpleMessage("覆写"), - "overrideDesc": MessageLookupByLibrary.simpleMessage("覆写代理相关配置"), - "overrideDns": MessageLookupByLibrary.simpleMessage("覆写DNS"), - "overrideDnsDesc": - MessageLookupByLibrary.simpleMessage("开启后将覆盖配置中的DNS选项"), - "password": MessageLookupByLibrary.simpleMessage("密码"), - "passwordTip": MessageLookupByLibrary.simpleMessage("密码不能为空"), - "paste": MessageLookupByLibrary.simpleMessage("粘贴"), - "pleaseBindWebDAV": MessageLookupByLibrary.simpleMessage("请绑定WebDAV"), - "pleaseInputAdminPassword": - MessageLookupByLibrary.simpleMessage("请输入管理员密码"), - "pleaseUploadFile": MessageLookupByLibrary.simpleMessage("请上传文件"), - "pleaseUploadValidQrcode": - MessageLookupByLibrary.simpleMessage("请上传有效的二维码"), - "port": MessageLookupByLibrary.simpleMessage("端口"), - "preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"), - "pressKeyboard": MessageLookupByLibrary.simpleMessage("请按下按键"), - "preview": MessageLookupByLibrary.simpleMessage("预览"), - "profile": MessageLookupByLibrary.simpleMessage("配置"), - "profileAutoUpdateIntervalInvalidValidationDesc": - MessageLookupByLibrary.simpleMessage("请输入有效间隔时间格式"), - "profileAutoUpdateIntervalNullValidationDesc": - MessageLookupByLibrary.simpleMessage("请输入自动更新间隔时间"), - "profileHasUpdate": - MessageLookupByLibrary.simpleMessage("配置文件已经修改,是否关闭自动更新 "), - "profileNameNullValidationDesc": - MessageLookupByLibrary.simpleMessage("请输入配置名称"), - "profileParseErrorDesc": - MessageLookupByLibrary.simpleMessage("配置文件解析错误"), - "profileUrlInvalidValidationDesc": - MessageLookupByLibrary.simpleMessage("请输入有效配置URL"), - "profileUrlNullValidationDesc": - MessageLookupByLibrary.simpleMessage("请输入配置URL"), - "profiles": MessageLookupByLibrary.simpleMessage("配置"), - "profilesSort": MessageLookupByLibrary.simpleMessage("配置排序"), - "project": MessageLookupByLibrary.simpleMessage("项目"), - "providers": MessageLookupByLibrary.simpleMessage("提供者"), - "proxies": MessageLookupByLibrary.simpleMessage("代理"), - "proxiesSetting": MessageLookupByLibrary.simpleMessage("代理设置"), - "proxyGroup": MessageLookupByLibrary.simpleMessage("代理组"), - "proxyNameserver": MessageLookupByLibrary.simpleMessage("代理域名服务器"), - "proxyNameserverDesc": - MessageLookupByLibrary.simpleMessage("用于解析代理节点的域名"), - "proxyPort": MessageLookupByLibrary.simpleMessage("代理端口"), - "proxyPortDesc": MessageLookupByLibrary.simpleMessage("设置Clash监听端口"), - "proxyProviders": MessageLookupByLibrary.simpleMessage("代理提供者"), - "prueBlackMode": MessageLookupByLibrary.simpleMessage("纯黑模式"), - "qrcode": MessageLookupByLibrary.simpleMessage("二维码"), - "qrcodeDesc": MessageLookupByLibrary.simpleMessage("扫描二维码获取配置文件"), - "recovery": MessageLookupByLibrary.simpleMessage("恢复"), - "recoveryAll": MessageLookupByLibrary.simpleMessage("恢复所有数据"), - "recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"), - "recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"), - "regExp": MessageLookupByLibrary.simpleMessage("正则"), - "remote": MessageLookupByLibrary.simpleMessage("远程"), - "remoteBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到WebDAV"), - "remoteRecoveryDesc": - MessageLookupByLibrary.simpleMessage("通过WebDAV恢复数据"), - "remove": MessageLookupByLibrary.simpleMessage("移除"), - "requests": MessageLookupByLibrary.simpleMessage("请求"), - "requestsDesc": MessageLookupByLibrary.simpleMessage("查看最近请求记录"), - "reset": MessageLookupByLibrary.simpleMessage("重置"), - "resetTip": MessageLookupByLibrary.simpleMessage("确定要重置吗?"), - "resources": MessageLookupByLibrary.simpleMessage("资源"), - "resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"), - "respectRules": MessageLookupByLibrary.simpleMessage("遵守规则"), - "respectRulesDesc": MessageLookupByLibrary.simpleMessage( - "DNS连接跟随rules,需配置proxy-server-nameserver"), - "routeAddress": MessageLookupByLibrary.simpleMessage("路由地址"), - "routeAddressDesc": MessageLookupByLibrary.simpleMessage("配置监听路由地址"), - "routeMode": MessageLookupByLibrary.simpleMessage("路由模式"), - "routeMode_bypassPrivate": - MessageLookupByLibrary.simpleMessage("绕过私有路由地址"), - "routeMode_config": MessageLookupByLibrary.simpleMessage("使用配置"), - "rule": MessageLookupByLibrary.simpleMessage("规则"), - "ruleProviders": MessageLookupByLibrary.simpleMessage("规则提供者"), - "save": MessageLookupByLibrary.simpleMessage("保存"), - "search": MessageLookupByLibrary.simpleMessage("搜索"), - "seconds": MessageLookupByLibrary.simpleMessage("秒"), - "selectAll": MessageLookupByLibrary.simpleMessage("全选"), - "selected": MessageLookupByLibrary.simpleMessage("已选择"), - "settings": MessageLookupByLibrary.simpleMessage("设置"), - "show": MessageLookupByLibrary.simpleMessage("显示"), - "shrink": MessageLookupByLibrary.simpleMessage("紧凑"), - "silentLaunch": MessageLookupByLibrary.simpleMessage("静默启动"), - "silentLaunchDesc": MessageLookupByLibrary.simpleMessage("后台启动"), - "size": MessageLookupByLibrary.simpleMessage("尺寸"), - "sort": MessageLookupByLibrary.simpleMessage("排序"), - "source": MessageLookupByLibrary.simpleMessage("来源"), - "stackMode": MessageLookupByLibrary.simpleMessage("栈模式"), - "standard": MessageLookupByLibrary.simpleMessage("标准"), - "start": MessageLookupByLibrary.simpleMessage("启动"), - "startVpn": MessageLookupByLibrary.simpleMessage("正在启动VPN..."), - "status": MessageLookupByLibrary.simpleMessage("状态"), - "statusDesc": MessageLookupByLibrary.simpleMessage("关闭后将使用系统DNS"), - "stop": MessageLookupByLibrary.simpleMessage("暂停"), - "stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."), - "style": MessageLookupByLibrary.simpleMessage("风格"), - "submit": MessageLookupByLibrary.simpleMessage("提交"), - "sync": MessageLookupByLibrary.simpleMessage("同步"), - "system": MessageLookupByLibrary.simpleMessage("系统"), - "systemFont": MessageLookupByLibrary.simpleMessage("系统字体"), - "systemProxy": MessageLookupByLibrary.simpleMessage("系统代理"), - "systemProxyDesc": MessageLookupByLibrary.simpleMessage("设置系统代理"), - "tab": MessageLookupByLibrary.simpleMessage("标签页"), - "tabAnimation": MessageLookupByLibrary.simpleMessage("选项卡动画"), - "tabAnimationDesc": - MessageLookupByLibrary.simpleMessage("开启后,主页选项卡将添加切换动画"), - "tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"), - "tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"), - "testUrl": MessageLookupByLibrary.simpleMessage("测速链接"), - "theme": MessageLookupByLibrary.simpleMessage("主题"), - "themeColor": MessageLookupByLibrary.simpleMessage("主题色彩"), - "themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"), - "themeMode": MessageLookupByLibrary.simpleMessage("主题模式"), - "threeColumns": MessageLookupByLibrary.simpleMessage("三列"), - "tight": MessageLookupByLibrary.simpleMessage("紧凑"), - "time": MessageLookupByLibrary.simpleMessage("时间"), - "tip": MessageLookupByLibrary.simpleMessage("提示"), - "toggle": MessageLookupByLibrary.simpleMessage("切换"), - "tools": MessageLookupByLibrary.simpleMessage("工具"), - "trafficUsage": MessageLookupByLibrary.simpleMessage("流量统计"), - "tun": MessageLookupByLibrary.simpleMessage("虚拟网卡"), - "tunDesc": MessageLookupByLibrary.simpleMessage("仅在管理员模式生效"), - "twoColumns": MessageLookupByLibrary.simpleMessage("两列"), - "unableToUpdateCurrentProfileDesc": - MessageLookupByLibrary.simpleMessage("无法更新当前配置文件"), - "unifiedDelay": MessageLookupByLibrary.simpleMessage("统一延迟"), - "unifiedDelayDesc": MessageLookupByLibrary.simpleMessage("去除握手等额外延迟"), - "unknown": MessageLookupByLibrary.simpleMessage("未知"), - "update": MessageLookupByLibrary.simpleMessage("更新"), - "upload": MessageLookupByLibrary.simpleMessage("上传"), - "url": MessageLookupByLibrary.simpleMessage("URL"), - "urlDesc": MessageLookupByLibrary.simpleMessage("通过URL获取配置文件"), - "useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"), - "useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"), - "value": MessageLookupByLibrary.simpleMessage("值"), - "view": MessageLookupByLibrary.simpleMessage("查看"), - "vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"), - "vpnEnableDesc": - MessageLookupByLibrary.simpleMessage("通过VpnService自动路由系统所有流量"), - "vpnSystemProxyDesc": - MessageLookupByLibrary.simpleMessage("为VpnService附加HTTP代理"), - "vpnTip": MessageLookupByLibrary.simpleMessage("重启VPN后改变生效"), - "webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV配置"), - "whitelistMode": MessageLookupByLibrary.simpleMessage("白名单模式"), - "years": MessageLookupByLibrary.simpleMessage("年"), - "zh_CN": MessageLookupByLibrary.simpleMessage("中文简体") - }; + "about": MessageLookupByLibrary.simpleMessage("关于"), + "accessControl": MessageLookupByLibrary.simpleMessage("访问控制"), + "accessControlAllowDesc": MessageLookupByLibrary.simpleMessage( + "只允许选中应用进入VPN", + ), + "accessControlDesc": MessageLookupByLibrary.simpleMessage("配置应用访问代理"), + "accessControlNotAllowDesc": MessageLookupByLibrary.simpleMessage( + "选中应用将会被排除在VPN之外", + ), + "account": MessageLookupByLibrary.simpleMessage("账号"), + "accountTip": MessageLookupByLibrary.simpleMessage("账号不能为空"), + "action": MessageLookupByLibrary.simpleMessage("操作"), + "action_mode": MessageLookupByLibrary.simpleMessage("切换模式"), + "action_proxy": MessageLookupByLibrary.simpleMessage("系统代理"), + "action_start": MessageLookupByLibrary.simpleMessage("启动/停止"), + "action_tun": MessageLookupByLibrary.simpleMessage("虚拟网卡"), + "action_view": MessageLookupByLibrary.simpleMessage("显示/隐藏"), + "add": MessageLookupByLibrary.simpleMessage("添加"), + "address": MessageLookupByLibrary.simpleMessage("地址"), + "addressHelp": MessageLookupByLibrary.simpleMessage("WebDAV服务器地址"), + "addressTip": MessageLookupByLibrary.simpleMessage("请输入有效的WebDAV地址"), + "adminAutoLaunch": MessageLookupByLibrary.simpleMessage("管理员自启动"), + "adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage("使用管理员模式开机自启动"), + "ago": MessageLookupByLibrary.simpleMessage("前"), + "agree": MessageLookupByLibrary.simpleMessage("同意"), + "allApps": MessageLookupByLibrary.simpleMessage("所有应用"), + "allowBypass": MessageLookupByLibrary.simpleMessage("允许应用绕过VPN"), + "allowBypassDesc": MessageLookupByLibrary.simpleMessage("开启后部分应用可绕过VPN"), + "allowLan": MessageLookupByLibrary.simpleMessage("局域网代理"), + "allowLanDesc": MessageLookupByLibrary.simpleMessage("允许通过局域网访问代理"), + "app": MessageLookupByLibrary.simpleMessage("应用"), + "appAccessControl": MessageLookupByLibrary.simpleMessage("应用访问控制"), + "appDesc": MessageLookupByLibrary.simpleMessage("处理应用相关设置"), + "application": MessageLookupByLibrary.simpleMessage("应用程序"), + "applicationDesc": MessageLookupByLibrary.simpleMessage("修改应用程序相关设置"), + "auto": MessageLookupByLibrary.simpleMessage("自动"), + "autoCheckUpdate": MessageLookupByLibrary.simpleMessage("自动检查更新"), + "autoCheckUpdateDesc": MessageLookupByLibrary.simpleMessage("应用启动时自动检查更新"), + "autoCloseConnections": MessageLookupByLibrary.simpleMessage("自动关闭连接"), + "autoCloseConnectionsDesc": MessageLookupByLibrary.simpleMessage( + "切换节点后自动关闭连接", + ), + "autoLaunch": MessageLookupByLibrary.simpleMessage("自启动"), + "autoLaunchDesc": MessageLookupByLibrary.simpleMessage("跟随系统自启动"), + "autoRun": MessageLookupByLibrary.simpleMessage("自动运行"), + "autoRunDesc": MessageLookupByLibrary.simpleMessage("应用打开时自动运行"), + "autoUpdate": MessageLookupByLibrary.simpleMessage("自动更新"), + "autoUpdateInterval": MessageLookupByLibrary.simpleMessage("自动更新间隔(分钟)"), + "backup": MessageLookupByLibrary.simpleMessage("备份"), + "backupAndRecovery": MessageLookupByLibrary.simpleMessage("备份与恢复"), + "backupAndRecoveryDesc": MessageLookupByLibrary.simpleMessage( + "通过WebDAV或者文件同步数据", + ), + "backupSuccess": MessageLookupByLibrary.simpleMessage("备份成功"), + "bind": MessageLookupByLibrary.simpleMessage("绑定"), + "blacklistMode": MessageLookupByLibrary.simpleMessage("黑名单模式"), + "bypassDomain": MessageLookupByLibrary.simpleMessage("排除域名"), + "bypassDomainDesc": MessageLookupByLibrary.simpleMessage("仅在系统代理启用时生效"), + "cacheCorrupt": MessageLookupByLibrary.simpleMessage("缓存已损坏,是否清空?"), + "cancel": MessageLookupByLibrary.simpleMessage("取消"), + "cancelFilterSystemApp": MessageLookupByLibrary.simpleMessage("取消过滤系统应用"), + "cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"), + "checkError": MessageLookupByLibrary.simpleMessage("检测失败"), + "checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"), + "checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"), + "checking": MessageLookupByLibrary.simpleMessage("检测中..."), + "clipboardExport": MessageLookupByLibrary.simpleMessage("导出剪贴板"), + "clipboardImport": MessageLookupByLibrary.simpleMessage("剪贴板导入"), + "columns": MessageLookupByLibrary.simpleMessage("列数"), + "compatible": MessageLookupByLibrary.simpleMessage("兼容模式"), + "compatibleDesc": MessageLookupByLibrary.simpleMessage( + "开启将失去部分应用能力,获得全量的Clash的支持", + ), + "confirm": MessageLookupByLibrary.simpleMessage("确定"), + "connections": MessageLookupByLibrary.simpleMessage("连接"), + "connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"), + "connectivity": MessageLookupByLibrary.simpleMessage("连通性:"), + "copy": MessageLookupByLibrary.simpleMessage("复制"), + "copyEnvVar": MessageLookupByLibrary.simpleMessage("复制环境变量"), + "copyLink": MessageLookupByLibrary.simpleMessage("复制链接"), + "copySuccess": MessageLookupByLibrary.simpleMessage("复制成功"), + "core": MessageLookupByLibrary.simpleMessage("内核"), + "coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"), + "country": MessageLookupByLibrary.simpleMessage("区域"), + "create": MessageLookupByLibrary.simpleMessage("创建"), + "cut": MessageLookupByLibrary.simpleMessage("剪切"), + "dark": MessageLookupByLibrary.simpleMessage("深色"), + "dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"), + "days": MessageLookupByLibrary.simpleMessage("天"), + "defaultNameserver": MessageLookupByLibrary.simpleMessage("默认域名服务器"), + "defaultNameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析DNS服务器"), + "defaultSort": MessageLookupByLibrary.simpleMessage("按默认排序"), + "defaultText": MessageLookupByLibrary.simpleMessage("默认"), + "delay": MessageLookupByLibrary.simpleMessage("延迟"), + "delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"), + "delete": MessageLookupByLibrary.simpleMessage("删除"), + "deleteProfileTip": MessageLookupByLibrary.simpleMessage("确定要删除当前配置吗?"), + "desc": MessageLookupByLibrary.simpleMessage( + "基于ClashMeta的多平台代理客户端,简单易用,开源无广告。", + ), + "direct": MessageLookupByLibrary.simpleMessage("直连"), + "disclaimer": MessageLookupByLibrary.simpleMessage("免责声明"), + "disclaimerDesc": MessageLookupByLibrary.simpleMessage( + "本软件仅供学习交流、科研等非商业性质的用途,严禁将本软件用于商业目的。如有任何商业行为,均与本软件无关。", + ), + "discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"), + "discovery": MessageLookupByLibrary.simpleMessage("发现新版本"), + "dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"), + "dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"), + "doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"), + "domain": MessageLookupByLibrary.simpleMessage("域名"), + "download": MessageLookupByLibrary.simpleMessage("下载"), + "edit": MessageLookupByLibrary.simpleMessage("编辑"), + "en": MessageLookupByLibrary.simpleMessage("英语"), + "entries": MessageLookupByLibrary.simpleMessage("个条目"), + "exclude": MessageLookupByLibrary.simpleMessage("从最近任务中隐藏"), + "excludeDesc": MessageLookupByLibrary.simpleMessage("应用在后台时,从最近任务中隐藏应用"), + "exit": MessageLookupByLibrary.simpleMessage("退出"), + "expand": MessageLookupByLibrary.simpleMessage("标准"), + "expirationTime": MessageLookupByLibrary.simpleMessage("到期时间"), + "exportFile": MessageLookupByLibrary.simpleMessage("导出文件"), + "exportLogs": MessageLookupByLibrary.simpleMessage("导出日志"), + "exportSuccess": MessageLookupByLibrary.simpleMessage("导出成功"), + "externalController": MessageLookupByLibrary.simpleMessage("外部控制器"), + "externalControllerDesc": MessageLookupByLibrary.simpleMessage( + "开启后将可以通过9090端口控制Clash内核", + ), + "externalLink": MessageLookupByLibrary.simpleMessage("外部链接"), + "externalResources": MessageLookupByLibrary.simpleMessage("外部资源"), + "fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip过滤"), + "fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip范围"), + "fallback": MessageLookupByLibrary.simpleMessage("Fallback"), + "fallbackDesc": MessageLookupByLibrary.simpleMessage("一般情况下使用境外DNS"), + "fallbackFilter": MessageLookupByLibrary.simpleMessage("Fallback过滤"), + "file": MessageLookupByLibrary.simpleMessage("文件"), + "fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"), + "fileIsUpdate": MessageLookupByLibrary.simpleMessage("文件有修改,是否保存修改"), + "filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"), + "findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"), + "findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后存在闪退风险"), + "fontFamily": MessageLookupByLibrary.simpleMessage("字体"), + "fourColumns": MessageLookupByLibrary.simpleMessage("四列"), + "general": MessageLookupByLibrary.simpleMessage("基础"), + "generalDesc": MessageLookupByLibrary.simpleMessage("覆写基础设置"), + "geoData": MessageLookupByLibrary.simpleMessage("地理数据"), + "geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"), + "geodataLoaderDesc": MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"), + "geoipCode": MessageLookupByLibrary.simpleMessage("Geoip代码"), + "global": MessageLookupByLibrary.simpleMessage("全局"), + "go": MessageLookupByLibrary.simpleMessage("前往"), + "goDownload": MessageLookupByLibrary.simpleMessage("前往下载"), + "hasCacheChange": MessageLookupByLibrary.simpleMessage("是否缓存修改"), + "hostsDesc": MessageLookupByLibrary.simpleMessage("追加Hosts"), + "hotkeyConflict": MessageLookupByLibrary.simpleMessage("快捷键冲突"), + "hotkeyManagement": MessageLookupByLibrary.simpleMessage("快捷键管理"), + "hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage("使用键盘控制应用程序"), + "hours": MessageLookupByLibrary.simpleMessage("小时"), + "icon": MessageLookupByLibrary.simpleMessage("图片"), + "iconConfiguration": MessageLookupByLibrary.simpleMessage("图片配置"), + "iconStyle": MessageLookupByLibrary.simpleMessage("图标样式"), + "importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"), + "infiniteTime": MessageLookupByLibrary.simpleMessage("长期有效"), + "init": MessageLookupByLibrary.simpleMessage("初始化"), + "inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("请输入正确的快捷键"), + "intelligentSelected": MessageLookupByLibrary.simpleMessage("智能选择"), + "intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"), + "ipcidr": MessageLookupByLibrary.simpleMessage("IP/掩码"), + "ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"), + "ipv6InboundDesc": MessageLookupByLibrary.simpleMessage("允许IPv6入站"), + "just": MessageLookupByLibrary.simpleMessage("刚刚"), + "keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"), + "key": MessageLookupByLibrary.simpleMessage("键"), + "language": MessageLookupByLibrary.simpleMessage("语言"), + "layout": MessageLookupByLibrary.simpleMessage("布局"), + "light": MessageLookupByLibrary.simpleMessage("浅色"), + "list": MessageLookupByLibrary.simpleMessage("列表"), + "local": MessageLookupByLibrary.simpleMessage("本地"), + "localBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到本地"), + "localRecoveryDesc": MessageLookupByLibrary.simpleMessage("通过文件恢复数据"), + "logLevel": MessageLookupByLibrary.simpleMessage("日志等级"), + "logcat": MessageLookupByLibrary.simpleMessage("日志捕获"), + "logcatDesc": MessageLookupByLibrary.simpleMessage("禁用将会隐藏日志入口"), + "logs": MessageLookupByLibrary.simpleMessage("日志"), + "logsDesc": MessageLookupByLibrary.simpleMessage("日志捕获记录"), + "loopback": MessageLookupByLibrary.simpleMessage("回环解锁工具"), + "loopbackDesc": MessageLookupByLibrary.simpleMessage("用于UWP回环解锁"), + "loose": MessageLookupByLibrary.simpleMessage("宽松"), + "memoryInfo": MessageLookupByLibrary.simpleMessage("内存信息"), + "min": MessageLookupByLibrary.simpleMessage("最小"), + "minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"), + "minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage("修改系统默认退出事件"), + "minutes": MessageLookupByLibrary.simpleMessage("分钟"), + "mode": MessageLookupByLibrary.simpleMessage("模式"), + "months": MessageLookupByLibrary.simpleMessage("月"), + "more": MessageLookupByLibrary.simpleMessage("更多"), + "name": MessageLookupByLibrary.simpleMessage("名称"), + "nameSort": MessageLookupByLibrary.simpleMessage("按名称排序"), + "nameserver": MessageLookupByLibrary.simpleMessage("域名服务器"), + "nameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析域名"), + "nameserverPolicy": MessageLookupByLibrary.simpleMessage("域名服务器策略"), + "nameserverPolicyDesc": MessageLookupByLibrary.simpleMessage("指定对应域名服务器策略"), + "network": MessageLookupByLibrary.simpleMessage("网络"), + "networkDesc": MessageLookupByLibrary.simpleMessage("修改网络相关设置"), + "networkDetection": MessageLookupByLibrary.simpleMessage("网络检测"), + "networkSpeed": MessageLookupByLibrary.simpleMessage("网络速度"), + "noData": MessageLookupByLibrary.simpleMessage("暂无数据"), + "noHotKey": MessageLookupByLibrary.simpleMessage("暂无快捷键"), + "noIcon": MessageLookupByLibrary.simpleMessage("无图标"), + "noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"), + "noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("暂无更多信息"), + "noNetwork": MessageLookupByLibrary.simpleMessage("无网络"), + "noProxy": MessageLookupByLibrary.simpleMessage("暂无代理"), + "noProxyDesc": MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"), + "notEmpty": MessageLookupByLibrary.simpleMessage("不能为空"), + "notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"), + "nullConnectionsDesc": MessageLookupByLibrary.simpleMessage("暂无连接"), + "nullCoreInfoDesc": MessageLookupByLibrary.simpleMessage("无法获取内核信息"), + "nullLogsDesc": MessageLookupByLibrary.simpleMessage("暂无日志"), + "nullProfileDesc": MessageLookupByLibrary.simpleMessage("没有配置文件,请先添加配置文件"), + "nullProxies": MessageLookupByLibrary.simpleMessage("暂无代理"), + "nullRequestsDesc": MessageLookupByLibrary.simpleMessage("暂无请求"), + "oneColumn": MessageLookupByLibrary.simpleMessage("一列"), + "onlyIcon": MessageLookupByLibrary.simpleMessage("仅图标"), + "onlyOtherApps": MessageLookupByLibrary.simpleMessage("仅第三方应用"), + "onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage("仅统计代理"), + "onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage( + "开启后,将只统计代理流量", + ), + "options": MessageLookupByLibrary.simpleMessage("选项"), + "other": MessageLookupByLibrary.simpleMessage("其他"), + "otherContributors": MessageLookupByLibrary.simpleMessage("其他贡献者"), + "outboundMode": MessageLookupByLibrary.simpleMessage("出站模式"), + "override": MessageLookupByLibrary.simpleMessage("覆写"), + "overrideDesc": MessageLookupByLibrary.simpleMessage("覆写代理相关配置"), + "overrideDns": MessageLookupByLibrary.simpleMessage("覆写DNS"), + "overrideDnsDesc": MessageLookupByLibrary.simpleMessage("开启后将覆盖配置中的DNS选项"), + "password": MessageLookupByLibrary.simpleMessage("密码"), + "passwordTip": MessageLookupByLibrary.simpleMessage("密码不能为空"), + "paste": MessageLookupByLibrary.simpleMessage("粘贴"), + "pleaseBindWebDAV": MessageLookupByLibrary.simpleMessage("请绑定WebDAV"), + "pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage( + "请输入管理员密码", + ), + "pleaseUploadFile": MessageLookupByLibrary.simpleMessage("请上传文件"), + "pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage( + "请上传有效的二维码", + ), + "port": MessageLookupByLibrary.simpleMessage("端口"), + "preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"), + "pressKeyboard": MessageLookupByLibrary.simpleMessage("请按下按键"), + "preview": MessageLookupByLibrary.simpleMessage("预览"), + "profile": MessageLookupByLibrary.simpleMessage("配置"), + "profileAutoUpdateIntervalInvalidValidationDesc": + MessageLookupByLibrary.simpleMessage("请输入有效间隔时间格式"), + "profileAutoUpdateIntervalNullValidationDesc": + MessageLookupByLibrary.simpleMessage("请输入自动更新间隔时间"), + "profileHasUpdate": MessageLookupByLibrary.simpleMessage( + "配置文件已经修改,是否关闭自动更新 ", + ), + "profileNameNullValidationDesc": MessageLookupByLibrary.simpleMessage( + "请输入配置名称", + ), + "profileParseErrorDesc": MessageLookupByLibrary.simpleMessage("配置文件解析错误"), + "profileUrlInvalidValidationDesc": MessageLookupByLibrary.simpleMessage( + "请输入有效配置URL", + ), + "profileUrlNullValidationDesc": MessageLookupByLibrary.simpleMessage( + "请输入配置URL", + ), + "profiles": MessageLookupByLibrary.simpleMessage("配置"), + "profilesSort": MessageLookupByLibrary.simpleMessage("配置排序"), + "project": MessageLookupByLibrary.simpleMessage("项目"), + "providers": MessageLookupByLibrary.simpleMessage("提供者"), + "proxies": MessageLookupByLibrary.simpleMessage("代理"), + "proxiesSetting": MessageLookupByLibrary.simpleMessage("代理设置"), + "proxyGroup": MessageLookupByLibrary.simpleMessage("代理组"), + "proxyNameserver": MessageLookupByLibrary.simpleMessage("代理域名服务器"), + "proxyNameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析代理节点的域名"), + "proxyPort": MessageLookupByLibrary.simpleMessage("代理端口"), + "proxyPortDesc": MessageLookupByLibrary.simpleMessage("设置Clash监听端口"), + "proxyProviders": MessageLookupByLibrary.simpleMessage("代理提供者"), + "prueBlackMode": MessageLookupByLibrary.simpleMessage("纯黑模式"), + "qrcode": MessageLookupByLibrary.simpleMessage("二维码"), + "qrcodeDesc": MessageLookupByLibrary.simpleMessage("扫描二维码获取配置文件"), + "recovery": MessageLookupByLibrary.simpleMessage("恢复"), + "recoveryAll": MessageLookupByLibrary.simpleMessage("恢复所有数据"), + "recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"), + "recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"), + "regExp": MessageLookupByLibrary.simpleMessage("正则"), + "remote": MessageLookupByLibrary.simpleMessage("远程"), + "remoteBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到WebDAV"), + "remoteRecoveryDesc": MessageLookupByLibrary.simpleMessage("通过WebDAV恢复数据"), + "remove": MessageLookupByLibrary.simpleMessage("移除"), + "requests": MessageLookupByLibrary.simpleMessage("请求"), + "requestsDesc": MessageLookupByLibrary.simpleMessage("查看最近请求记录"), + "reset": MessageLookupByLibrary.simpleMessage("重置"), + "resetTip": MessageLookupByLibrary.simpleMessage("确定要重置吗?"), + "resources": MessageLookupByLibrary.simpleMessage("资源"), + "resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"), + "respectRules": MessageLookupByLibrary.simpleMessage("遵守规则"), + "respectRulesDesc": MessageLookupByLibrary.simpleMessage( + "DNS连接跟随rules,需配置proxy-server-nameserver", + ), + "routeAddress": MessageLookupByLibrary.simpleMessage("路由地址"), + "routeAddressDesc": MessageLookupByLibrary.simpleMessage("配置监听路由地址"), + "routeMode": MessageLookupByLibrary.simpleMessage("路由模式"), + "routeMode_bypassPrivate": MessageLookupByLibrary.simpleMessage("绕过私有路由地址"), + "routeMode_config": MessageLookupByLibrary.simpleMessage("使用配置"), + "rule": MessageLookupByLibrary.simpleMessage("规则"), + "ruleProviders": MessageLookupByLibrary.simpleMessage("规则提供者"), + "save": MessageLookupByLibrary.simpleMessage("保存"), + "search": MessageLookupByLibrary.simpleMessage("搜索"), + "seconds": MessageLookupByLibrary.simpleMessage("秒"), + "selectAll": MessageLookupByLibrary.simpleMessage("全选"), + "selected": MessageLookupByLibrary.simpleMessage("已选择"), + "settings": MessageLookupByLibrary.simpleMessage("设置"), + "show": MessageLookupByLibrary.simpleMessage("显示"), + "shrink": MessageLookupByLibrary.simpleMessage("紧凑"), + "silentLaunch": MessageLookupByLibrary.simpleMessage("静默启动"), + "silentLaunchDesc": MessageLookupByLibrary.simpleMessage("后台启动"), + "size": MessageLookupByLibrary.simpleMessage("尺寸"), + "sort": MessageLookupByLibrary.simpleMessage("排序"), + "source": MessageLookupByLibrary.simpleMessage("来源"), + "stackMode": MessageLookupByLibrary.simpleMessage("栈模式"), + "standard": MessageLookupByLibrary.simpleMessage("标准"), + "start": MessageLookupByLibrary.simpleMessage("启动"), + "startVpn": MessageLookupByLibrary.simpleMessage("正在启动VPN..."), + "status": MessageLookupByLibrary.simpleMessage("状态"), + "statusDesc": MessageLookupByLibrary.simpleMessage("关闭后将使用系统DNS"), + "stop": MessageLookupByLibrary.simpleMessage("暂停"), + "stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."), + "style": MessageLookupByLibrary.simpleMessage("风格"), + "submit": MessageLookupByLibrary.simpleMessage("提交"), + "sync": MessageLookupByLibrary.simpleMessage("同步"), + "system": MessageLookupByLibrary.simpleMessage("系统"), + "systemFont": MessageLookupByLibrary.simpleMessage("系统字体"), + "systemProxy": MessageLookupByLibrary.simpleMessage("系统代理"), + "systemProxyDesc": MessageLookupByLibrary.simpleMessage("设置系统代理"), + "tab": MessageLookupByLibrary.simpleMessage("标签页"), + "tabAnimation": MessageLookupByLibrary.simpleMessage("选项卡动画"), + "tabAnimationDesc": MessageLookupByLibrary.simpleMessage( + "开启后,主页选项卡将添加切换动画", + ), + "tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"), + "tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"), + "testUrl": MessageLookupByLibrary.simpleMessage("测速链接"), + "theme": MessageLookupByLibrary.simpleMessage("主题"), + "themeColor": MessageLookupByLibrary.simpleMessage("主题色彩"), + "themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"), + "themeMode": MessageLookupByLibrary.simpleMessage("主题模式"), + "threeColumns": MessageLookupByLibrary.simpleMessage("三列"), + "tight": MessageLookupByLibrary.simpleMessage("紧凑"), + "time": MessageLookupByLibrary.simpleMessage("时间"), + "tip": MessageLookupByLibrary.simpleMessage("提示"), + "toggle": MessageLookupByLibrary.simpleMessage("切换"), + "tools": MessageLookupByLibrary.simpleMessage("工具"), + "trafficUsage": MessageLookupByLibrary.simpleMessage("流量统计"), + "tun": MessageLookupByLibrary.simpleMessage("虚拟网卡"), + "tunDesc": MessageLookupByLibrary.simpleMessage("仅在管理员模式生效"), + "twoColumns": MessageLookupByLibrary.simpleMessage("两列"), + "unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage( + "无法更新当前配置文件", + ), + "unifiedDelay": MessageLookupByLibrary.simpleMessage("统一延迟"), + "unifiedDelayDesc": MessageLookupByLibrary.simpleMessage("去除握手等额外延迟"), + "unknown": MessageLookupByLibrary.simpleMessage("未知"), + "update": MessageLookupByLibrary.simpleMessage("更新"), + "upload": MessageLookupByLibrary.simpleMessage("上传"), + "url": MessageLookupByLibrary.simpleMessage("URL"), + "urlDesc": MessageLookupByLibrary.simpleMessage("通过URL获取配置文件"), + "useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"), + "useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"), + "value": MessageLookupByLibrary.simpleMessage("值"), + "view": MessageLookupByLibrary.simpleMessage("查看"), + "vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"), + "vpnEnableDesc": MessageLookupByLibrary.simpleMessage( + "通过VpnService自动路由系统所有流量", + ), + "vpnSystemProxyDesc": MessageLookupByLibrary.simpleMessage( + "为VpnService附加HTTP代理", + ), + "vpnTip": MessageLookupByLibrary.simpleMessage("重启VPN后改变生效"), + "webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV配置"), + "whitelistMode": MessageLookupByLibrary.simpleMessage("白名单模式"), + "years": MessageLookupByLibrary.simpleMessage("年"), + "zh_CN": MessageLookupByLibrary.simpleMessage("中文简体"), + }; } diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 095ea699..f8f2eb59 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -18,17 +18,20 @@ class AppLocalizations { static AppLocalizations? _current; static AppLocalizations get current { - assert(_current != null, - 'No instance of AppLocalizations was loaded. Try to initialize the AppLocalizations delegate before accessing AppLocalizations.current.'); + assert( + _current != null, + 'No instance of AppLocalizations was loaded. Try to initialize the AppLocalizations delegate before accessing AppLocalizations.current.', + ); return _current!; } static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); static Future load(Locale locale) { - final name = (locale.countryCode?.isEmpty ?? false) - ? locale.languageCode - : locale.toString(); + final name = + (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; @@ -41,8 +44,10 @@ class AppLocalizations { static AppLocalizations of(BuildContext context) { final instance = AppLocalizations.maybeOf(context); - assert(instance != null, - 'No instance of AppLocalizations present in the widget tree. Did you add AppLocalizations.delegate in localizationsDelegates?'); + assert( + instance != null, + 'No instance of AppLocalizations present in the widget tree. Did you add AppLocalizations.delegate in localizationsDelegates?', + ); return instance!; } @@ -52,92 +57,47 @@ class AppLocalizations { /// `Rule` String get rule { - return Intl.message( - 'Rule', - name: 'rule', - desc: '', - args: [], - ); + return Intl.message('Rule', name: 'rule', desc: '', args: []); } /// `Global` String get global { - return Intl.message( - 'Global', - name: 'global', - desc: '', - args: [], - ); + return Intl.message('Global', name: 'global', desc: '', args: []); } /// `Direct` String get direct { - return Intl.message( - 'Direct', - name: 'direct', - desc: '', - args: [], - ); + return Intl.message('Direct', name: 'direct', desc: '', args: []); } /// `Dashboard` String get dashboard { - return Intl.message( - 'Dashboard', - name: 'dashboard', - desc: '', - args: [], - ); + return Intl.message('Dashboard', name: 'dashboard', desc: '', args: []); } /// `Proxies` String get proxies { - return Intl.message( - 'Proxies', - name: 'proxies', - desc: '', - args: [], - ); + return Intl.message('Proxies', name: 'proxies', desc: '', args: []); } /// `Profile` String get profile { - return Intl.message( - 'Profile', - name: 'profile', - desc: '', - args: [], - ); + return Intl.message('Profile', name: 'profile', desc: '', args: []); } /// `Profiles` String get profiles { - return Intl.message( - 'Profiles', - name: 'profiles', - desc: '', - args: [], - ); + return Intl.message('Profiles', name: 'profiles', desc: '', args: []); } /// `Tools` String get tools { - return Intl.message( - 'Tools', - name: 'tools', - desc: '', - args: [], - ); + return Intl.message('Tools', name: 'tools', desc: '', args: []); } /// `Logs` String get logs { - return Intl.message( - 'Logs', - name: 'logs', - desc: '', - args: [], - ); + return Intl.message('Logs', name: 'logs', desc: '', args: []); } /// `Log capture records` @@ -152,12 +112,7 @@ class AppLocalizations { /// `Resources` String get resources { - return Intl.message( - 'Resources', - name: 'resources', - desc: '', - args: [], - ); + return Intl.message('Resources', name: 'resources', desc: '', args: []); } /// `External resource related info` @@ -182,12 +137,7 @@ class AppLocalizations { /// `Core info` String get coreInfo { - return Intl.message( - 'Core info', - name: 'coreInfo', - desc: '', - args: [], - ); + return Intl.message('Core info', name: 'coreInfo', desc: '', args: []); } /// `Unable to obtain core info` @@ -232,32 +182,17 @@ class AppLocalizations { /// `Upload` String get upload { - return Intl.message( - 'Upload', - name: 'upload', - desc: '', - args: [], - ); + return Intl.message('Upload', name: 'upload', desc: '', args: []); } /// `Download` String get download { - return Intl.message( - 'Download', - name: 'download', - desc: '', - args: [], - ); + return Intl.message('Download', name: 'download', desc: '', args: []); } /// `No proxy` String get noProxy { - return Intl.message( - 'No proxy', - name: 'noProxy', - desc: '', - args: [], - ); + return Intl.message('No proxy', name: 'noProxy', desc: '', args: []); } /// `Please create a profile or add a valid profile` @@ -282,82 +217,42 @@ class AppLocalizations { /// `No logs` String get nullLogsDesc { - return Intl.message( - 'No logs', - name: 'nullLogsDesc', - desc: '', - args: [], - ); + return Intl.message('No logs', name: 'nullLogsDesc', desc: '', args: []); } /// `Settings` String get settings { - return Intl.message( - 'Settings', - name: 'settings', - desc: '', - args: [], - ); + return Intl.message('Settings', name: 'settings', desc: '', args: []); } /// `Language` String get language { - return Intl.message( - 'Language', - name: 'language', - desc: '', - args: [], - ); + return Intl.message('Language', name: 'language', desc: '', args: []); } /// `Default` String get defaultText { - return Intl.message( - 'Default', - name: 'defaultText', - desc: '', - args: [], - ); + return Intl.message('Default', name: 'defaultText', desc: '', args: []); } /// `More` String get more { - return Intl.message( - 'More', - name: 'more', - desc: '', - args: [], - ); + return Intl.message('More', name: 'more', desc: '', args: []); } /// `Other` String get other { - return Intl.message( - 'Other', - name: 'other', - desc: '', - args: [], - ); + return Intl.message('Other', name: 'other', desc: '', args: []); } /// `About` String get about { - return Intl.message( - 'About', - name: 'about', - desc: '', - args: [], - ); + return Intl.message('About', name: 'about', desc: '', args: []); } /// `English` String get en { - return Intl.message( - 'English', - name: 'en', - desc: '', - args: [], - ); + return Intl.message('English', name: 'en', desc: '', args: []); } /// `Simplified Chinese` @@ -372,12 +267,7 @@ class AppLocalizations { /// `Theme` String get theme { - return Intl.message( - 'Theme', - name: 'theme', - desc: '', - args: [], - ); + return Intl.message('Theme', name: 'theme', desc: '', args: []); } /// `Set dark mode,adjust the color` @@ -392,12 +282,7 @@ class AppLocalizations { /// `Override` String get override { - return Intl.message( - 'Override', - name: 'override', - desc: '', - args: [], - ); + return Intl.message('Override', name: 'override', desc: '', args: []); } /// `Override Proxy related config` @@ -412,12 +297,7 @@ class AppLocalizations { /// `AllowLan` String get allowLan { - return Intl.message( - 'AllowLan', - name: 'allowLan', - desc: '', - args: [], - ); + return Intl.message('AllowLan', name: 'allowLan', desc: '', args: []); } /// `Allow access proxy through the LAN` @@ -432,12 +312,7 @@ class AppLocalizations { /// `TUN` String get tun { - return Intl.message( - 'TUN', - name: 'tun', - desc: '', - args: [], - ); + return Intl.message('TUN', name: 'tun', desc: '', args: []); } /// `only effective in administrator mode` @@ -472,12 +347,7 @@ class AppLocalizations { /// `Auto launch` String get autoLaunch { - return Intl.message( - 'Auto launch', - name: 'autoLaunch', - desc: '', - args: [], - ); + return Intl.message('Auto launch', name: 'autoLaunch', desc: '', args: []); } /// `Follow the system self startup` @@ -512,12 +382,7 @@ class AppLocalizations { /// `AutoRun` String get autoRun { - return Intl.message( - 'AutoRun', - name: 'autoRun', - desc: '', - args: [], - ); + return Intl.message('AutoRun', name: 'autoRun', desc: '', args: []); } /// `Auto run when the application is opened` @@ -532,12 +397,7 @@ class AppLocalizations { /// `Logcat` String get logcat { - return Intl.message( - 'Logcat', - name: 'logcat', - desc: '', - args: [], - ); + return Intl.message('Logcat', name: 'logcat', desc: '', args: []); } /// `Disabling will hide the log entry` @@ -592,12 +452,7 @@ class AppLocalizations { /// `Application` String get application { - return Intl.message( - 'Application', - name: 'application', - desc: '', - args: [], - ); + return Intl.message('Application', name: 'application', desc: '', args: []); } /// `Modify application related settings` @@ -612,152 +467,77 @@ class AppLocalizations { /// `Edit` String get edit { - return Intl.message( - 'Edit', - name: 'edit', - desc: '', - args: [], - ); + return Intl.message('Edit', name: 'edit', desc: '', args: []); } /// `Confirm` String get confirm { - return Intl.message( - 'Confirm', - name: 'confirm', - desc: '', - args: [], - ); + return Intl.message('Confirm', name: 'confirm', desc: '', args: []); } /// `Update` String get update { - return Intl.message( - 'Update', - name: 'update', - desc: '', - args: [], - ); + return Intl.message('Update', name: 'update', desc: '', args: []); } /// `Add` String get add { - return Intl.message( - 'Add', - name: 'add', - desc: '', - args: [], - ); + return Intl.message('Add', name: 'add', desc: '', args: []); } /// `Save` String get save { - return Intl.message( - 'Save', - name: 'save', - desc: '', - args: [], - ); + return Intl.message('Save', name: 'save', desc: '', args: []); } /// `Delete` String get delete { - return Intl.message( - 'Delete', - name: 'delete', - desc: '', - args: [], - ); + return Intl.message('Delete', name: 'delete', desc: '', args: []); } /// `Years` String get years { - return Intl.message( - 'Years', - name: 'years', - desc: '', - args: [], - ); + return Intl.message('Years', name: 'years', desc: '', args: []); } /// `Months` String get months { - return Intl.message( - 'Months', - name: 'months', - desc: '', - args: [], - ); + return Intl.message('Months', name: 'months', desc: '', args: []); } /// `Hours` String get hours { - return Intl.message( - 'Hours', - name: 'hours', - desc: '', - args: [], - ); + return Intl.message('Hours', name: 'hours', desc: '', args: []); } /// `Days` String get days { - return Intl.message( - 'Days', - name: 'days', - desc: '', - args: [], - ); + return Intl.message('Days', name: 'days', desc: '', args: []); } /// `Minutes` String get minutes { - return Intl.message( - 'Minutes', - name: 'minutes', - desc: '', - args: [], - ); + return Intl.message('Minutes', name: 'minutes', desc: '', args: []); } /// `Seconds` String get seconds { - return Intl.message( - 'Seconds', - name: 'seconds', - desc: '', - args: [], - ); + return Intl.message('Seconds', name: 'seconds', desc: '', args: []); } /// ` Ago` String get ago { - return Intl.message( - ' Ago', - name: 'ago', - desc: '', - args: [], - ); + return Intl.message(' Ago', name: 'ago', desc: '', args: []); } /// `Just` String get just { - return Intl.message( - 'Just', - name: 'just', - desc: '', - args: [], - ); + return Intl.message('Just', name: 'just', desc: '', args: []); } /// `QR code` String get qrcode { - return Intl.message( - 'QR code', - name: 'qrcode', - desc: '', - args: [], - ); + return Intl.message('QR code', name: 'qrcode', desc: '', args: []); } /// `Scan QR code to obtain profile` @@ -772,12 +552,7 @@ class AppLocalizations { /// `URL` String get url { - return Intl.message( - 'URL', - name: 'url', - desc: '', - args: [], - ); + return Intl.message('URL', name: 'url', desc: '', args: []); } /// `Obtain profile through URL` @@ -792,12 +567,7 @@ class AppLocalizations { /// `File` String get file { - return Intl.message( - 'File', - name: 'file', - desc: '', - args: [], - ); + return Intl.message('File', name: 'file', desc: '', args: []); } /// `Directly upload profile` @@ -812,12 +582,7 @@ class AppLocalizations { /// `Name` String get name { - return Intl.message( - 'Name', - name: 'name', - desc: '', - args: [], - ); + return Intl.message('Name', name: 'name', desc: '', args: []); } /// `Please input the profile name` @@ -852,12 +617,7 @@ class AppLocalizations { /// `Auto update` String get autoUpdate { - return Intl.message( - 'Auto update', - name: 'autoUpdate', - desc: '', - args: [], - ); + return Intl.message('Auto update', name: 'autoUpdate', desc: '', args: []); } /// `Auto update interval (minutes)` @@ -892,62 +652,32 @@ class AppLocalizations { /// `Theme mode` String get themeMode { - return Intl.message( - 'Theme mode', - name: 'themeMode', - desc: '', - args: [], - ); + return Intl.message('Theme mode', name: 'themeMode', desc: '', args: []); } /// `Theme color` String get themeColor { - return Intl.message( - 'Theme color', - name: 'themeColor', - desc: '', - args: [], - ); + return Intl.message('Theme color', name: 'themeColor', desc: '', args: []); } /// `Preview` String get preview { - return Intl.message( - 'Preview', - name: 'preview', - desc: '', - args: [], - ); + return Intl.message('Preview', name: 'preview', desc: '', args: []); } /// `Auto` String get auto { - return Intl.message( - 'Auto', - name: 'auto', - desc: '', - args: [], - ); + return Intl.message('Auto', name: 'auto', desc: '', args: []); } /// `Light` String get light { - return Intl.message( - 'Light', - name: 'light', - desc: '', - args: [], - ); + return Intl.message('Light', name: 'light', desc: '', args: []); } /// `Dark` String get dark { - return Intl.message( - 'Dark', - name: 'dark', - desc: '', - args: [], - ); + return Intl.message('Dark', name: 'dark', desc: '', args: []); } /// `Import from URL` @@ -962,12 +692,7 @@ class AppLocalizations { /// `Submit` String get submit { - return Intl.message( - 'Submit', - name: 'submit', - desc: '', - args: [], - ); + return Intl.message('Submit', name: 'submit', desc: '', args: []); } /// `Do you want to pass` @@ -982,12 +707,7 @@ class AppLocalizations { /// `Create` String get create { - return Intl.message( - 'Create', - name: 'create', - desc: '', - args: [], - ); + return Intl.message('Create', name: 'create', desc: '', args: []); } /// `Sort by default` @@ -1002,22 +722,12 @@ class AppLocalizations { /// `Sort by delay` String get delaySort { - return Intl.message( - 'Sort by delay', - name: 'delaySort', - desc: '', - args: [], - ); + return Intl.message('Sort by delay', name: 'delaySort', desc: '', args: []); } /// `Sort by name` String get nameSort { - return Intl.message( - 'Sort by name', - name: 'nameSort', - desc: '', - args: [], - ); + return Intl.message('Sort by name', name: 'nameSort', desc: '', args: []); } /// `Please upload file` @@ -1082,12 +792,7 @@ class AppLocalizations { /// `Select all` String get selectAll { - return Intl.message( - 'Select all', - name: 'selectAll', - desc: '', - args: [], - ); + return Intl.message('Select all', name: 'selectAll', desc: '', args: []); } /// `Cancel select all` @@ -1132,12 +837,7 @@ class AppLocalizations { /// `Selected` String get selected { - return Intl.message( - 'Selected', - name: 'selected', - desc: '', - args: [], - ); + return Intl.message('Selected', name: 'selected', desc: '', args: []); } /// `unable to update current profile` @@ -1172,12 +872,7 @@ class AppLocalizations { /// `ProxyPort` String get proxyPort { - return Intl.message( - 'ProxyPort', - name: 'proxyPort', - desc: '', - args: [], - ); + return Intl.message('ProxyPort', name: 'proxyPort', desc: '', args: []); } /// `Set the Clash listening port` @@ -1192,42 +887,22 @@ class AppLocalizations { /// `Port` String get port { - return Intl.message( - 'Port', - name: 'port', - desc: '', - args: [], - ); + return Intl.message('Port', name: 'port', desc: '', args: []); } /// `LogLevel` String get logLevel { - return Intl.message( - 'LogLevel', - name: 'logLevel', - desc: '', - args: [], - ); + return Intl.message('LogLevel', name: 'logLevel', desc: '', args: []); } /// `Show` String get show { - return Intl.message( - 'Show', - name: 'show', - desc: '', - args: [], - ); + return Intl.message('Show', name: 'show', desc: '', args: []); } /// `Exit` String get exit { - return Intl.message( - 'Exit', - name: 'exit', - desc: '', - args: [], - ); + return Intl.message('Exit', name: 'exit', desc: '', args: []); } /// `System proxy` @@ -1242,22 +917,12 @@ class AppLocalizations { /// `Project` String get project { - return Intl.message( - 'Project', - name: 'project', - desc: '', - args: [], - ); + return Intl.message('Project', name: 'project', desc: '', args: []); } /// `Core` String get core { - return Intl.message( - 'Core', - name: 'core', - desc: '', - args: [], - ); + return Intl.message('Core', name: 'core', desc: '', args: []); } /// `Tab animation` @@ -1302,12 +967,7 @@ class AppLocalizations { /// `Stopping VPN...` String get stopVpn { - return Intl.message( - 'Stopping VPN...', - name: 'stopVpn', - desc: '', - args: [], - ); + return Intl.message('Stopping VPN...', name: 'stopVpn', desc: '', args: []); } /// `Discovery a new version` @@ -1352,12 +1012,7 @@ class AppLocalizations { /// `tip` String get tip { - return Intl.message( - 'tip', - name: 'tip', - desc: '', - args: [], - ); + return Intl.message('tip', name: 'tip', desc: '', args: []); } /// `Backup and Recovery` @@ -1382,32 +1037,17 @@ class AppLocalizations { /// `Account` String get account { - return Intl.message( - 'Account', - name: 'account', - desc: '', - args: [], - ); + return Intl.message('Account', name: 'account', desc: '', args: []); } /// `Backup` String get backup { - return Intl.message( - 'Backup', - name: 'backup', - desc: '', - args: [], - ); + return Intl.message('Backup', name: 'backup', desc: '', args: []); } /// `Recovery` String get recovery { - return Intl.message( - 'Recovery', - name: 'recovery', - desc: '', - args: [], - ); + return Intl.message('Recovery', name: 'recovery', desc: '', args: []); } /// `Only recovery profiles` @@ -1452,12 +1092,7 @@ class AppLocalizations { /// `No info` String get noInfo { - return Intl.message( - 'No info', - name: 'noInfo', - desc: '', - args: [], - ); + return Intl.message('No info', name: 'noInfo', desc: '', args: []); } /// `Please bind WebDAV` @@ -1472,12 +1107,7 @@ class AppLocalizations { /// `Bind` String get bind { - return Intl.message( - 'Bind', - name: 'bind', - desc: '', - args: [], - ); + return Intl.message('Bind', name: 'bind', desc: '', args: []); } /// `Connectivity:` @@ -1502,12 +1132,7 @@ class AppLocalizations { /// `Address` String get address { - return Intl.message( - 'Address', - name: 'address', - desc: '', - args: [], - ); + return Intl.message('Address', name: 'address', desc: '', args: []); } /// `WebDAV server address` @@ -1532,12 +1157,7 @@ class AppLocalizations { /// `Password` String get password { - return Intl.message( - 'Password', - name: 'password', - desc: '', - args: [], - ); + return Intl.message('Password', name: 'password', desc: '', args: []); } /// `Password cannot be empty` @@ -1602,22 +1222,12 @@ class AppLocalizations { /// `Unknown` String get unknown { - return Intl.message( - 'Unknown', - name: 'unknown', - desc: '', - args: [], - ); + return Intl.message('Unknown', name: 'unknown', desc: '', args: []); } /// `GeoData` String get geoData { - return Intl.message( - 'GeoData', - name: 'geoData', - desc: '', - args: [], - ); + return Intl.message('GeoData', name: 'geoData', desc: '', args: []); } /// `External resources` @@ -1632,42 +1242,22 @@ class AppLocalizations { /// `Checking...` String get checking { - return Intl.message( - 'Checking...', - name: 'checking', - desc: '', - args: [], - ); + return Intl.message('Checking...', name: 'checking', desc: '', args: []); } /// `Country` String get country { - return Intl.message( - 'Country', - name: 'country', - desc: '', - args: [], - ); + return Intl.message('Country', name: 'country', desc: '', args: []); } /// `Check error` String get checkError { - return Intl.message( - 'Check error', - name: 'checkError', - desc: '', - args: [], - ); + return Intl.message('Check error', name: 'checkError', desc: '', args: []); } /// `Search` String get search { - return Intl.message( - 'Search', - name: 'search', - desc: '', - args: [], - ); + return Intl.message('Search', name: 'search', desc: '', args: []); } /// `Allow applications to bypass VPN` @@ -1722,22 +1312,12 @@ class AppLocalizations { /// `App` String get app { - return Intl.message( - 'App', - name: 'app', - desc: '', - args: [], - ); + return Intl.message('App', name: 'app', desc: '', args: []); } /// `General` String get general { - return Intl.message( - 'General', - name: 'general', - desc: '', - args: [], - ); + return Intl.message('General', name: 'general', desc: '', args: []); } /// `Attach HTTP proxy to VpnService` @@ -1822,12 +1402,7 @@ class AppLocalizations { /// `Requests` String get requests { - return Intl.message( - 'Requests', - name: 'requests', - desc: '', - args: [], - ); + return Intl.message('Requests', name: 'requests', desc: '', args: []); } /// `View recently request records` @@ -1862,12 +1437,7 @@ class AppLocalizations { /// `Init` String get init { - return Intl.message( - 'Init', - name: 'init', - desc: '', - args: [], - ); + return Intl.message('Init', name: 'init', desc: '', args: []); } /// `Long term effective` @@ -1892,12 +1462,7 @@ class AppLocalizations { /// `Connections` String get connections { - return Intl.message( - 'Connections', - name: 'connections', - desc: '', - args: [], - ); + return Intl.message('Connections', name: 'connections', desc: '', args: []); } /// `View current connections data` @@ -1932,72 +1497,37 @@ class AppLocalizations { /// `Intranet IP` String get intranetIP { - return Intl.message( - 'Intranet IP', - name: 'intranetIP', - desc: '', - args: [], - ); + return Intl.message('Intranet IP', name: 'intranetIP', desc: '', args: []); } /// `View` String get view { - return Intl.message( - 'View', - name: 'view', - desc: '', - args: [], - ); + return Intl.message('View', name: 'view', desc: '', args: []); } /// `Cut` String get cut { - return Intl.message( - 'Cut', - name: 'cut', - desc: '', - args: [], - ); + return Intl.message('Cut', name: 'cut', desc: '', args: []); } /// `Copy` String get copy { - return Intl.message( - 'Copy', - name: 'copy', - desc: '', - args: [], - ); + return Intl.message('Copy', name: 'copy', desc: '', args: []); } /// `Paste` String get paste { - return Intl.message( - 'Paste', - name: 'paste', - desc: '', - args: [], - ); + return Intl.message('Paste', name: 'paste', desc: '', args: []); } /// `Test url` String get testUrl { - return Intl.message( - 'Test url', - name: 'testUrl', - desc: '', - args: [], - ); + return Intl.message('Test url', name: 'testUrl', desc: '', args: []); } /// `Sync` String get sync { - return Intl.message( - 'Sync', - name: 'sync', - desc: '', - args: [], - ); + return Intl.message('Sync', name: 'sync', desc: '', args: []); } /// `Hidden from recent tasks` @@ -2022,22 +1552,12 @@ class AppLocalizations { /// `One column` String get oneColumn { - return Intl.message( - 'One column', - name: 'oneColumn', - desc: '', - args: [], - ); + return Intl.message('One column', name: 'oneColumn', desc: '', args: []); } /// `Two columns` String get twoColumns { - return Intl.message( - 'Two columns', - name: 'twoColumns', - desc: '', - args: [], - ); + return Intl.message('Two columns', name: 'twoColumns', desc: '', args: []); } /// `Three columns` @@ -2062,102 +1582,52 @@ class AppLocalizations { /// `Standard` String get expand { - return Intl.message( - 'Standard', - name: 'expand', - desc: '', - args: [], - ); + return Intl.message('Standard', name: 'expand', desc: '', args: []); } /// `Shrink` String get shrink { - return Intl.message( - 'Shrink', - name: 'shrink', - desc: '', - args: [], - ); + return Intl.message('Shrink', name: 'shrink', desc: '', args: []); } /// `Min` String get min { - return Intl.message( - 'Min', - name: 'min', - desc: '', - args: [], - ); + return Intl.message('Min', name: 'min', desc: '', args: []); } - /// `Tab` - String get tab { - return Intl.message( - 'Tab', - name: 'tab', - desc: '', - args: [], - ); + /// `Tab` + String get tab { + return Intl.message('Tab', name: 'tab', desc: '', args: []); } /// `List` String get list { - return Intl.message( - 'List', - name: 'list', - desc: '', - args: [], - ); + return Intl.message('List', name: 'list', desc: '', args: []); } /// `Delay` String get delay { - return Intl.message( - 'Delay', - name: 'delay', - desc: '', - args: [], - ); + return Intl.message('Delay', name: 'delay', desc: '', args: []); } /// `Style` String get style { - return Intl.message( - 'Style', - name: 'style', - desc: '', - args: [], - ); + return Intl.message('Style', name: 'style', desc: '', args: []); } /// `Size` String get size { - return Intl.message( - 'Size', - name: 'size', - desc: '', - args: [], - ); + return Intl.message('Size', name: 'size', desc: '', args: []); } /// `Sort` String get sort { - return Intl.message( - 'Sort', - name: 'sort', - desc: '', - args: [], - ); + return Intl.message('Sort', name: 'sort', desc: '', args: []); } /// `Columns` String get columns { - return Intl.message( - 'Columns', - name: 'columns', - desc: '', - args: [], - ); + return Intl.message('Columns', name: 'columns', desc: '', args: []); } /// `Proxies setting` @@ -2172,22 +1642,12 @@ class AppLocalizations { /// `Proxy group` String get proxyGroup { - return Intl.message( - 'Proxy group', - name: 'proxyGroup', - desc: '', - args: [], - ); + return Intl.message('Proxy group', name: 'proxyGroup', desc: '', args: []); } /// `Go` String get go { - return Intl.message( - 'Go', - name: 'go', - desc: '', - args: [], - ); + return Intl.message('Go', name: 'go', desc: '', args: []); } /// `External link` @@ -2282,32 +1742,17 @@ class AppLocalizations { /// ` entries` String get entries { - return Intl.message( - ' entries', - name: 'entries', - desc: '', - args: [], - ); + return Intl.message(' entries', name: 'entries', desc: '', args: []); } /// `Local` String get local { - return Intl.message( - 'Local', - name: 'local', - desc: '', - args: [], - ); + return Intl.message('Local', name: 'local', desc: '', args: []); } /// `Remote` String get remote { - return Intl.message( - 'Remote', - name: 'remote', - desc: '', - args: [], - ); + return Intl.message('Remote', name: 'remote', desc: '', args: []); } /// `Backup local data to WebDAV` @@ -2352,42 +1797,22 @@ class AppLocalizations { /// `Mode` String get mode { - return Intl.message( - 'Mode', - name: 'mode', - desc: '', - args: [], - ); + return Intl.message('Mode', name: 'mode', desc: '', args: []); } /// `Time` String get time { - return Intl.message( - 'Time', - name: 'time', - desc: '', - args: [], - ); + return Intl.message('Time', name: 'time', desc: '', args: []); } /// `Source` String get source { - return Intl.message( - 'Source', - name: 'source', - desc: '', - args: [], - ); + return Intl.message('Source', name: 'source', desc: '', args: []); } /// `All apps` String get allApps { - return Intl.message( - 'All apps', - name: 'allApps', - desc: '', - args: [], - ); + return Intl.message('All apps', name: 'allApps', desc: '', args: []); } /// `Only third-party apps` @@ -2402,12 +1827,7 @@ class AppLocalizations { /// `Action` String get action { - return Intl.message( - 'Action', - name: 'action', - desc: '', - args: [], - ); + return Intl.message('Action', name: 'action', desc: '', args: []); } /// `Intelligent selection` @@ -2442,42 +1862,22 @@ class AppLocalizations { /// `Layout` String get layout { - return Intl.message( - 'Layout', - name: 'layout', - desc: '', - args: [], - ); + return Intl.message('Layout', name: 'layout', desc: '', args: []); } /// `Tight` String get tight { - return Intl.message( - 'Tight', - name: 'tight', - desc: '', - args: [], - ); + return Intl.message('Tight', name: 'tight', desc: '', args: []); } /// `Standard` String get standard { - return Intl.message( - 'Standard', - name: 'standard', - desc: '', - args: [], - ); + return Intl.message('Standard', name: 'standard', desc: '', args: []); } /// `Loose` String get loose { - return Intl.message( - 'Loose', - name: 'loose', - desc: '', - args: [], - ); + return Intl.message('Loose', name: 'loose', desc: '', args: []); } /// `Profiles sort` @@ -2492,22 +1892,12 @@ class AppLocalizations { /// `Start` String get start { - return Intl.message( - 'Start', - name: 'start', - desc: '', - args: [], - ); + return Intl.message('Start', name: 'start', desc: '', args: []); } /// `Stop` String get stop { - return Intl.message( - 'Stop', - name: 'stop', - desc: '', - args: [], - ); + return Intl.message('Stop', name: 'stop', desc: '', args: []); } /// `Processing app related settings` @@ -2552,22 +1942,12 @@ class AppLocalizations { /// `Key` String get key { - return Intl.message( - 'Key', - name: 'key', - desc: '', - args: [], - ); + return Intl.message('Key', name: 'key', desc: '', args: []); } /// `Value` String get value { - return Intl.message( - 'Value', - name: 'value', - desc: '', - args: [], - ); + return Intl.message('Value', name: 'value', desc: '', args: []); } /// `Cannot be empty` @@ -2582,12 +1962,7 @@ class AppLocalizations { /// `Add Hosts` String get hostsDesc { - return Intl.message( - 'Add Hosts', - name: 'hostsDesc', - desc: '', - args: [], - ); + return Intl.message('Add Hosts', name: 'hostsDesc', desc: '', args: []); } /// `Changes take effect after restarting the VPN` @@ -2612,12 +1987,7 @@ class AppLocalizations { /// `Options` String get options { - return Intl.message( - 'Options', - name: 'options', - desc: '', - args: [], - ); + return Intl.message('Options', name: 'options', desc: '', args: []); } /// `Loopback unlock tool` @@ -2642,12 +2012,7 @@ class AppLocalizations { /// `Providers` String get providers { - return Intl.message( - 'Providers', - name: 'providers', - desc: '', - args: [], - ); + return Intl.message('Providers', name: 'providers', desc: '', args: []); } /// `Proxy providers` @@ -2692,12 +2057,7 @@ class AppLocalizations { /// `Status` String get status { - return Intl.message( - 'Status', - name: 'status', - desc: '', - args: [], - ); + return Intl.message('Status', name: 'status', desc: '', args: []); } /// `System DNS will be used when turned off` @@ -2742,12 +2102,7 @@ class AppLocalizations { /// `DNS mode` String get dnsMode { - return Intl.message( - 'DNS mode', - name: 'dnsMode', - desc: '', - args: [], - ); + return Intl.message('DNS mode', name: 'dnsMode', desc: '', args: []); } /// `Fakeip range` @@ -2792,12 +2147,7 @@ class AppLocalizations { /// `Nameserver` String get nameserver { - return Intl.message( - 'Nameserver', - name: 'nameserver', - desc: '', - args: [], - ); + return Intl.message('Nameserver', name: 'nameserver', desc: '', args: []); } /// `For resolving domain` @@ -2812,12 +2162,7 @@ class AppLocalizations { /// `Use hosts` String get useHosts { - return Intl.message( - 'Use hosts', - name: 'useHosts', - desc: '', - args: [], - ); + return Intl.message('Use hosts', name: 'useHosts', desc: '', args: []); } /// `Use system hosts` @@ -2872,12 +2217,7 @@ class AppLocalizations { /// `Fallback` String get fallback { - return Intl.message( - 'Fallback', - name: 'fallback', - desc: '', - args: [], - ); + return Intl.message('Fallback', name: 'fallback', desc: '', args: []); } /// `Generally use offshore DNS` @@ -2902,72 +2242,37 @@ class AppLocalizations { /// `Geoip code` String get geoipCode { - return Intl.message( - 'Geoip code', - name: 'geoipCode', - desc: '', - args: [], - ); + return Intl.message('Geoip code', name: 'geoipCode', desc: '', args: []); } /// `Ipcidr` String get ipcidr { - return Intl.message( - 'Ipcidr', - name: 'ipcidr', - desc: '', - args: [], - ); + return Intl.message('Ipcidr', name: 'ipcidr', desc: '', args: []); } /// `Domain` String get domain { - return Intl.message( - 'Domain', - name: 'domain', - desc: '', - args: [], - ); + return Intl.message('Domain', name: 'domain', desc: '', args: []); } /// `Reset` String get reset { - return Intl.message( - 'Reset', - name: 'reset', - desc: '', - args: [], - ); + return Intl.message('Reset', name: 'reset', desc: '', args: []); } /// `Show/Hide` String get action_view { - return Intl.message( - 'Show/Hide', - name: 'action_view', - desc: '', - args: [], - ); + return Intl.message('Show/Hide', name: 'action_view', desc: '', args: []); } /// `Start/Stop` String get action_start { - return Intl.message( - 'Start/Stop', - name: 'action_start', - desc: '', - args: [], - ); + return Intl.message('Start/Stop', name: 'action_start', desc: '', args: []); } /// `Switch mode` String get action_mode { - return Intl.message( - 'Switch mode', - name: 'action_mode', - desc: '', - args: [], - ); + return Intl.message('Switch mode', name: 'action_mode', desc: '', args: []); } /// `System proxy` @@ -2982,22 +2287,12 @@ class AppLocalizations { /// `TUN` String get action_tun { - return Intl.message( - 'TUN', - name: 'action_tun', - desc: '', - args: [], - ); + return Intl.message('TUN', name: 'action_tun', desc: '', args: []); } /// `Disclaimer` String get disclaimer { - return Intl.message( - 'Disclaimer', - name: 'disclaimer', - desc: '', - args: [], - ); + return Intl.message('Disclaimer', name: 'disclaimer', desc: '', args: []); } /// `This software is only used for non-commercial purposes such as learning exchanges and scientific research. It is strictly prohibited to use this software for commercial purposes. Any commercial activity, if any, has nothing to do with this software.` @@ -3012,12 +2307,7 @@ class AppLocalizations { /// `Agree` String get agree { - return Intl.message( - 'Agree', - name: 'agree', - desc: '', - args: [], - ); + return Intl.message('Agree', name: 'agree', desc: '', args: []); } /// `Hotkey Management` @@ -3072,32 +2362,17 @@ class AppLocalizations { /// `Remove` String get remove { - return Intl.message( - 'Remove', - name: 'remove', - desc: '', - args: [], - ); + return Intl.message('Remove', name: 'remove', desc: '', args: []); } /// `No HotKey` String get noHotKey { - return Intl.message( - 'No HotKey', - name: 'noHotKey', - desc: '', - args: [], - ); + return Intl.message('No HotKey', name: 'noHotKey', desc: '', args: []); } /// `No network` String get noNetwork { - return Intl.message( - 'No network', - name: 'noNetwork', - desc: '', - args: [], - ); + return Intl.message('No network', name: 'noNetwork', desc: '', args: []); } /// `Allow IPv6 inbound` @@ -3112,12 +2387,7 @@ class AppLocalizations { /// `Export logs` String get exportLogs { - return Intl.message( - 'Export logs', - name: 'exportLogs', - desc: '', - args: [], - ); + return Intl.message('Export logs', name: 'exportLogs', desc: '', args: []); } /// `Export Success` @@ -3132,52 +2402,27 @@ class AppLocalizations { /// `Icon style` String get iconStyle { - return Intl.message( - 'Icon style', - name: 'iconStyle', - desc: '', - args: [], - ); + return Intl.message('Icon style', name: 'iconStyle', desc: '', args: []); } /// `Icon` String get onlyIcon { - return Intl.message( - 'Icon', - name: 'onlyIcon', - desc: '', - args: [], - ); + return Intl.message('Icon', name: 'onlyIcon', desc: '', args: []); } /// `None` String get noIcon { - return Intl.message( - 'None', - name: 'noIcon', - desc: '', - args: [], - ); + return Intl.message('None', name: 'noIcon', desc: '', args: []); } /// `Stack mode` String get stackMode { - return Intl.message( - 'Stack mode', - name: 'stackMode', - desc: '', - args: [], - ); + return Intl.message('Stack mode', name: 'stackMode', desc: '', args: []); } /// `Network` String get network { - return Intl.message( - 'Network', - name: 'network', - desc: '', - args: [], - ); + return Intl.message('Network', name: 'network', desc: '', args: []); } /// `Modify network-related settings` @@ -3222,22 +2467,12 @@ class AppLocalizations { /// `RegExp` String get regExp { - return Intl.message( - 'RegExp', - name: 'regExp', - desc: '', - args: [], - ); + return Intl.message('RegExp', name: 'regExp', desc: '', args: []); } /// `Icon` String get icon { - return Intl.message( - 'Icon', - name: 'icon', - desc: '', - args: [], - ); + return Intl.message('Icon', name: 'icon', desc: '', args: []); } /// `Icon configuration` @@ -3252,12 +2487,7 @@ class AppLocalizations { /// `No data` String get noData { - return Intl.message( - 'No data', - name: 'noData', - desc: '', - args: [], - ); + return Intl.message('No data', name: 'noData', desc: '', args: []); } /// `Admin auto launch` @@ -3282,52 +2512,27 @@ class AppLocalizations { /// `FontFamily` String get fontFamily { - return Intl.message( - 'FontFamily', - name: 'fontFamily', - desc: '', - args: [], - ); + return Intl.message('FontFamily', name: 'fontFamily', desc: '', args: []); } /// `System font` String get systemFont { - return Intl.message( - 'System font', - name: 'systemFont', - desc: '', - args: [], - ); + return Intl.message('System font', name: 'systemFont', desc: '', args: []); } /// `Toggle` String get toggle { - return Intl.message( - 'Toggle', - name: 'toggle', - desc: '', - args: [], - ); + return Intl.message('Toggle', name: 'toggle', desc: '', args: []); } /// `System` String get system { - return Intl.message( - 'System', - name: 'system', - desc: '', - args: [], - ); + return Intl.message('System', name: 'system', desc: '', args: []); } /// `Route mode` String get routeMode { - return Intl.message( - 'Route mode', - name: 'routeMode', - desc: '', - args: [], - ); + return Intl.message('Route mode', name: 'routeMode', desc: '', args: []); } /// `Bypass private route address` @@ -3392,22 +2597,12 @@ class AppLocalizations { /// `Memory info` String get memoryInfo { - return Intl.message( - 'Memory info', - name: 'memoryInfo', - desc: '', - args: [], - ); + return Intl.message('Memory info', name: 'memoryInfo', desc: '', args: []); } /// `Cancel` String get cancel { - return Intl.message( - 'Cancel', - name: 'cancel', - desc: '', - args: [], - ); + return Intl.message('Cancel', name: 'cancel', desc: '', args: []); } /// `The file has been modified. Do you want to save the changes?` @@ -3442,12 +2637,7 @@ class AppLocalizations { /// `No proxies` String get nullProxies { - return Intl.message( - 'No proxies', - name: 'nullProxies', - desc: '', - args: [], - ); + return Intl.message('No proxies', name: 'nullProxies', desc: '', args: []); } /// `Copy success` @@ -3462,19 +2652,19 @@ class AppLocalizations { /// `Copy link` String get copyLink { - return Intl.message( - 'Copy link', - name: 'copyLink', - desc: '', - args: [], - ); + return Intl.message('Copy link', name: 'copyLink', desc: '', args: []); } /// `Export file` String get exportFile { + return Intl.message('Export file', name: 'exportFile', desc: '', args: []); + } + + /// `The cache is corrupt. Do you want to clear it?` + String get cacheCorrupt { return Intl.message( - 'Export file', - name: 'exportFile', + 'The cache is corrupt. Do you want to clear it?', + name: 'cacheCorrupt', desc: '', args: [], ); diff --git a/lib/main.dart b/lib/main.dart index b746a1d6..f104a60f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,11 +27,11 @@ Future main() async { globalState.packageInfo = await PackageInfo.fromPlatform(); final version = await system.version; final config = await preferences.getConfig() ?? Config(); + final clashConfig = await preferences.getClashConfig() ?? ClashConfig(); await AppLocalizations.load( other.getLocaleForString(config.appSetting.locale) ?? WidgetsBinding.instance.platformDispatcher.locale, ); - final clashConfig = await preferences.getClashConfig() ?? ClashConfig(); await android?.init(); await window?.init(config.windowProps, version); final appState = AppState( @@ -89,7 +89,7 @@ Future _service(List flags) async { await ClashCore.initGeo(); globalState.packageInfo = await PackageInfo.fromPlatform(); final clashConfig = await preferences.getClashConfig() ?? ClashConfig(); - final homeDirPath = await appPath.getHomeDirPath(); + final homeDirPath = await appPath.homeDirPath; await app?.tip(appLocalizations.startVpn); clashLibHandler .quickStart( diff --git a/lib/manager/app_state_manager.dart b/lib/manager/app_state_manager.dart index 1b1782a6..f996a02e 100644 --- a/lib/manager/app_state_manager.dart +++ b/lib/manager/app_state_manager.dart @@ -21,10 +21,11 @@ class _AppStateManagerState extends State _updateNavigationsContainer(Widget child) { return Selector2( selector: (_, appState, config) { + final group = appState.currentGroups; final hasProfile = config.profiles.isNotEmpty; return UpdateNavigationsSelector( openLogs: config.appSetting.openLogs, - hasProxies: hasProfile && config.currentProfileId != null, + hasProxies: group.isNotEmpty && hasProfile, ); }, builder: (context, state, child) { @@ -91,7 +92,7 @@ class _AppStateManagerState extends State @override Widget build(BuildContext context) { return Listener( - onPointerDown: (_) { + onPointerHover: (_) { render?.resume(); }, child: _cacheStateChange( diff --git a/lib/manager/window_manager.dart b/lib/manager/window_manager.dart index e6333a5a..715bb0df 100644 --- a/lib/manager/window_manager.dart +++ b/lib/manager/window_manager.dart @@ -113,7 +113,6 @@ class _WindowContainerState extends State @override Future onTaskbarCreated() async { globalState.appController.updateTray(true); - await globalState.appController.restartCore(); super.onTaskbarCreated(); } diff --git a/lib/models/app.dart b/lib/models/app.dart index 55615d82..599fd56d 100644 --- a/lib/models/app.dart +++ b/lib/models/app.dart @@ -20,7 +20,7 @@ class AppState with ChangeNotifier { SelectedMap _selectedMap; List _groups; double _viewWidth; - List _requests; + final FixedList _requests; num _checkIpNum; List _providers; List _packages; @@ -31,14 +31,17 @@ class AppState with ChangeNotifier { required Mode mode, required SelectedMap selectedMap, required int version, - }) : _navigationItems = [], + }) + : _navigationItems = [], _isInit = false, _currentLabel = "dashboard", - _viewWidth = other.getScreenSize().width, + _viewWidth = other + .getScreenSize() + .width, _selectedMap = selectedMap, _sortNum = 0, _checkIpNum = 0, - _requests = [], + _requests = FixedList(1000), _mode = mode, _brightness = null, _delayMap = {}, @@ -76,7 +79,7 @@ class AppState with ChangeNotifier { return navigationItems .where( (element) => element.modes.contains(navigationItemMode), - ) + ) .toList(); } @@ -106,7 +109,7 @@ class AppState with ChangeNotifier { if (index == -1) return proxyName; final group = groups[index]; final currentSelectedName = - group.getCurrentSelectedName(selectedMap[proxyName] ?? ''); + group.getCurrentSelectedName(selectedMap[proxyName] ?? ''); if (currentSelectedName.isEmpty) return proxyName; return getRealProxyName( currentSelectedName, @@ -131,19 +134,10 @@ class AppState with ChangeNotifier { } } - List get requests => _requests; - - set requests(List value) { - if (_requests != value) { - _requests = value; - notifyListeners(); - } - } + List get requests => _requests.list; addRequest(Connection value) { - _requests = List.from(_requests)..add(value); - const maxLength = 1000; - _requests = _requests.safeSublist(_requests.length - maxLength); + _requests.add(value); notifyListeners(); } @@ -273,13 +267,14 @@ class AppState with ChangeNotifier { if (provider == null) return; final index = _providers.indexWhere((item) => item.name == provider.name); if (index == -1) return; - _providers = List.from(_providers)..[index] = provider; + _providers = List.from(_providers) + ..[index] = provider; notifyListeners(); } Group? getGroupWithName(String groupName) { final index = - currentGroups.indexWhere((element) => element.name == groupName); + currentGroups.indexWhere((element) => element.name == groupName); return index != -1 ? currentGroups[index] : null; } @@ -304,13 +299,13 @@ class AppState with ChangeNotifier { class AppFlowingState with ChangeNotifier { int? _runTime; - List _logs; + final FixedList _logs; List _traffics; Traffic _totalTraffic; String? _localIp; AppFlowingState() - : _logs = [], + : _logs = FixedList(1000), _traffics = [], _totalTraffic = Traffic(); @@ -325,19 +320,10 @@ class AppFlowingState with ChangeNotifier { } } - List get logs => _logs; - - set logs(List value) { - if (_logs != value) { - _logs = value; - notifyListeners(); - } - } + List get logs => _logs.list; addLog(Log log) { - _logs = List.from(_logs)..add(log); - const maxLength = 1000; - _logs = _logs.safeSublist(_logs.length - maxLength); + _logs.add(log); notifyListeners(); } @@ -351,7 +337,8 @@ class AppFlowingState with ChangeNotifier { } addTraffic(Traffic traffic) { - _traffics = List.from(_traffics)..add(traffic); + _traffics = List.from(_traffics) + ..add(traffic); const maxLength = 30; _traffics = _traffics.safeSublist(_traffics.length - maxLength); notifyListeners(); diff --git a/lib/models/common.dart b/lib/models/common.dart index f12c3908..2917bb0a 100644 --- a/lib/models/common.dart +++ b/lib/models/common.dart @@ -8,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'generated/common.freezed.dart'; - part 'generated/common.g.dart'; @freezed @@ -31,7 +30,7 @@ class Package with _$Package { required String packageName, required String label, required bool isSystem, - required int firstInstallTime, + required int lastUpdateTime, }) = _Package; factory Package.fromJson(Map json) => @@ -71,6 +70,19 @@ class Connection with _$Connection { _$ConnectionFromJson(json); } +extension ConnectionExt on Connection { + String get desc { + var text = "${metadata.network}://"; + final ips = [ + metadata.host, + metadata.destinationIP, + ].where((ip) => ip.isNotEmpty); + text += ips.join("/"); + text += ":${metadata.destinationPort}"; + return text; + } +} + @JsonSerializable() class Log { @JsonKey(name: "LogLevel") @@ -101,42 +113,58 @@ class Log { } @freezed -class LogsAndKeywords with _$LogsAndKeywords { - const factory LogsAndKeywords({ +class LogsState with _$LogsState { + const factory LogsState({ @Default([]) List logs, @Default([]) List keywords, - }) = _LogsAndKeywords; - - factory LogsAndKeywords.fromJson(Map json) => - _$LogsAndKeywordsFromJson(json); + @Default("") String query, + }) = _LogsState; } -extension LogsAndKeywordsExt on LogsAndKeywords { - List get filteredLogs => logs - .where( - (log) => {log.logLevel.name}.containsAll(keywords), - ) - .toList(); +extension LogsStateExt on LogsState { + List get list { + final lowQuery = query.toLowerCase(); + return logs.where( + (log) { + final payload = log.payload?.toLowerCase(); + final logLevelName = log.logLevel.name; + return {logLevelName}.containsAll(keywords) && + ((payload?.contains(lowQuery) ?? false) || + logLevelName.contains(lowQuery)); + }, + ).toList(); + } } @freezed -class ConnectionsAndKeywords with _$ConnectionsAndKeywords { - const factory ConnectionsAndKeywords({ +class ConnectionsState with _$ConnectionsState { + const factory ConnectionsState({ @Default([]) List connections, @Default([]) List keywords, - }) = _ConnectionsAndKeywords; - - factory ConnectionsAndKeywords.fromJson(Map json) => - _$ConnectionsAndKeywordsFromJson(json); + @Default("") String query, + }) = _ConnectionsState; } -extension ConnectionsAndKeywordsExt on ConnectionsAndKeywords { - List get filteredConnections => connections - .where((connection) => { - ...connection.chains, - connection.metadata.process, - }.containsAll(keywords)) - .toList(); +extension ConnectionsStateExt on ConnectionsState { + List get list { + final lowerQuery = query.toLowerCase().trim(); + final lowQuery = query.toLowerCase(); + return connections.where((connection) { + final chains = connection.chains; + final process = connection.metadata.process; + final networkText = connection.metadata.network.toLowerCase(); + final hostText = connection.metadata.host.toLowerCase(); + final destinationIPText = connection.metadata.destinationIP.toLowerCase(); + final processText = connection.metadata.process.toLowerCase(); + final chainsText = chains.join("").toLowerCase(); + return {...chains, process}.containsAll(keywords) && + (networkText.contains(lowerQuery) || + hostText.contains(lowerQuery) || + destinationIPText.contains(lowQuery) || + processText.contains(lowerQuery) || + chainsText.contains(lowerQuery)); + }).toList(); + } } const defaultDavFileName = "backup.zip"; diff --git a/lib/models/generated/common.freezed.dart b/lib/models/generated/common.freezed.dart index c0851123..d92ec81d 100644 --- a/lib/models/generated/common.freezed.dart +++ b/lib/models/generated/common.freezed.dart @@ -290,7 +290,7 @@ mixin _$Package { String get packageName => throw _privateConstructorUsedError; String get label => throw _privateConstructorUsedError; bool get isSystem => throw _privateConstructorUsedError; - int get firstInstallTime => throw _privateConstructorUsedError; + int get lastUpdateTime => throw _privateConstructorUsedError; /// Serializes this Package to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -307,7 +307,7 @@ abstract class $PackageCopyWith<$Res> { _$PackageCopyWithImpl<$Res, Package>; @useResult $Res call( - {String packageName, String label, bool isSystem, int firstInstallTime}); + {String packageName, String label, bool isSystem, int lastUpdateTime}); } /// @nodoc @@ -328,7 +328,7 @@ class _$PackageCopyWithImpl<$Res, $Val extends Package> Object? packageName = null, Object? label = null, Object? isSystem = null, - Object? firstInstallTime = null, + Object? lastUpdateTime = null, }) { return _then(_value.copyWith( packageName: null == packageName @@ -343,9 +343,9 @@ class _$PackageCopyWithImpl<$Res, $Val extends Package> ? _value.isSystem : isSystem // ignore: cast_nullable_to_non_nullable as bool, - firstInstallTime: null == firstInstallTime - ? _value.firstInstallTime - : firstInstallTime // ignore: cast_nullable_to_non_nullable + lastUpdateTime: null == lastUpdateTime + ? _value.lastUpdateTime + : lastUpdateTime // ignore: cast_nullable_to_non_nullable as int, ) as $Val); } @@ -359,7 +359,7 @@ abstract class _$$PackageImplCopyWith<$Res> implements $PackageCopyWith<$Res> { @override @useResult $Res call( - {String packageName, String label, bool isSystem, int firstInstallTime}); + {String packageName, String label, bool isSystem, int lastUpdateTime}); } /// @nodoc @@ -378,7 +378,7 @@ class __$$PackageImplCopyWithImpl<$Res> Object? packageName = null, Object? label = null, Object? isSystem = null, - Object? firstInstallTime = null, + Object? lastUpdateTime = null, }) { return _then(_$PackageImpl( packageName: null == packageName @@ -393,9 +393,9 @@ class __$$PackageImplCopyWithImpl<$Res> ? _value.isSystem : isSystem // ignore: cast_nullable_to_non_nullable as bool, - firstInstallTime: null == firstInstallTime - ? _value.firstInstallTime - : firstInstallTime // ignore: cast_nullable_to_non_nullable + lastUpdateTime: null == lastUpdateTime + ? _value.lastUpdateTime + : lastUpdateTime // ignore: cast_nullable_to_non_nullable as int, )); } @@ -408,7 +408,7 @@ class _$PackageImpl implements _Package { {required this.packageName, required this.label, required this.isSystem, - required this.firstInstallTime}); + required this.lastUpdateTime}); factory _$PackageImpl.fromJson(Map json) => _$$PackageImplFromJson(json); @@ -420,11 +420,11 @@ class _$PackageImpl implements _Package { @override final bool isSystem; @override - final int firstInstallTime; + final int lastUpdateTime; @override String toString() { - return 'Package(packageName: $packageName, label: $label, isSystem: $isSystem, firstInstallTime: $firstInstallTime)'; + return 'Package(packageName: $packageName, label: $label, isSystem: $isSystem, lastUpdateTime: $lastUpdateTime)'; } @override @@ -437,14 +437,14 @@ class _$PackageImpl implements _Package { (identical(other.label, label) || other.label == label) && (identical(other.isSystem, isSystem) || other.isSystem == isSystem) && - (identical(other.firstInstallTime, firstInstallTime) || - other.firstInstallTime == firstInstallTime)); + (identical(other.lastUpdateTime, lastUpdateTime) || + other.lastUpdateTime == lastUpdateTime)); } @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => - Object.hash(runtimeType, packageName, label, isSystem, firstInstallTime); + Object.hash(runtimeType, packageName, label, isSystem, lastUpdateTime); /// Create a copy of Package /// with the given fields replaced by the non-null parameter values. @@ -467,7 +467,7 @@ abstract class _Package implements Package { {required final String packageName, required final String label, required final bool isSystem, - required final int firstInstallTime}) = _$PackageImpl; + required final int lastUpdateTime}) = _$PackageImpl; factory _Package.fromJson(Map json) = _$PackageImpl.fromJson; @@ -478,7 +478,7 @@ abstract class _Package implements Package { @override bool get isSystem; @override - int get firstInstallTime; + int get lastUpdateTime; /// Create a copy of Package /// with the given fields replaced by the non-null parameter values. @@ -1092,51 +1092,45 @@ abstract class _Connection implements Connection { throw _privateConstructorUsedError; } -LogsAndKeywords _$LogsAndKeywordsFromJson(Map json) { - return _LogsAndKeywords.fromJson(json); -} - /// @nodoc -mixin _$LogsAndKeywords { +mixin _$LogsState { List get logs => throw _privateConstructorUsedError; List get keywords => throw _privateConstructorUsedError; + String get query => throw _privateConstructorUsedError; - /// Serializes this LogsAndKeywords to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of LogsAndKeywords + /// Create a copy of LogsState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) - $LogsAndKeywordsCopyWith get copyWith => + $LogsStateCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $LogsAndKeywordsCopyWith<$Res> { - factory $LogsAndKeywordsCopyWith( - LogsAndKeywords value, $Res Function(LogsAndKeywords) then) = - _$LogsAndKeywordsCopyWithImpl<$Res, LogsAndKeywords>; +abstract class $LogsStateCopyWith<$Res> { + factory $LogsStateCopyWith(LogsState value, $Res Function(LogsState) then) = + _$LogsStateCopyWithImpl<$Res, LogsState>; @useResult - $Res call({List logs, List keywords}); + $Res call({List logs, List keywords, String query}); } /// @nodoc -class _$LogsAndKeywordsCopyWithImpl<$Res, $Val extends LogsAndKeywords> - implements $LogsAndKeywordsCopyWith<$Res> { - _$LogsAndKeywordsCopyWithImpl(this._value, this._then); +class _$LogsStateCopyWithImpl<$Res, $Val extends LogsState> + implements $LogsStateCopyWith<$Res> { + _$LogsStateCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; // ignore: unused_field final $Res Function($Val) _then; - /// Create a copy of LogsAndKeywords + /// Create a copy of LogsState /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? logs = null, Object? keywords = null, + Object? query = null, }) { return _then(_value.copyWith( logs: null == logs @@ -1147,38 +1141,43 @@ class _$LogsAndKeywordsCopyWithImpl<$Res, $Val extends LogsAndKeywords> ? _value.keywords : keywords // ignore: cast_nullable_to_non_nullable as List, + query: null == query + ? _value.query + : query // ignore: cast_nullable_to_non_nullable + as String, ) as $Val); } } /// @nodoc -abstract class _$$LogsAndKeywordsImplCopyWith<$Res> - implements $LogsAndKeywordsCopyWith<$Res> { - factory _$$LogsAndKeywordsImplCopyWith(_$LogsAndKeywordsImpl value, - $Res Function(_$LogsAndKeywordsImpl) then) = - __$$LogsAndKeywordsImplCopyWithImpl<$Res>; +abstract class _$$LogsStateImplCopyWith<$Res> + implements $LogsStateCopyWith<$Res> { + factory _$$LogsStateImplCopyWith( + _$LogsStateImpl value, $Res Function(_$LogsStateImpl) then) = + __$$LogsStateImplCopyWithImpl<$Res>; @override @useResult - $Res call({List logs, List keywords}); + $Res call({List logs, List keywords, String query}); } /// @nodoc -class __$$LogsAndKeywordsImplCopyWithImpl<$Res> - extends _$LogsAndKeywordsCopyWithImpl<$Res, _$LogsAndKeywordsImpl> - implements _$$LogsAndKeywordsImplCopyWith<$Res> { - __$$LogsAndKeywordsImplCopyWithImpl( - _$LogsAndKeywordsImpl _value, $Res Function(_$LogsAndKeywordsImpl) _then) +class __$$LogsStateImplCopyWithImpl<$Res> + extends _$LogsStateCopyWithImpl<$Res, _$LogsStateImpl> + implements _$$LogsStateImplCopyWith<$Res> { + __$$LogsStateImplCopyWithImpl( + _$LogsStateImpl _value, $Res Function(_$LogsStateImpl) _then) : super(_value, _then); - /// Create a copy of LogsAndKeywords + /// Create a copy of LogsState /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? logs = null, Object? keywords = null, + Object? query = null, }) { - return _then(_$LogsAndKeywordsImpl( + return _then(_$LogsStateImpl( logs: null == logs ? _value._logs : logs // ignore: cast_nullable_to_non_nullable @@ -1187,21 +1186,24 @@ class __$$LogsAndKeywordsImplCopyWithImpl<$Res> ? _value._keywords : keywords // ignore: cast_nullable_to_non_nullable as List, + query: null == query + ? _value.query + : query // ignore: cast_nullable_to_non_nullable + as String, )); } } /// @nodoc -@JsonSerializable() -class _$LogsAndKeywordsImpl implements _LogsAndKeywords { - const _$LogsAndKeywordsImpl( - {final List logs = const [], final List keywords = const []}) + +class _$LogsStateImpl implements _LogsState { + const _$LogsStateImpl( + {final List logs = const [], + final List keywords = const [], + this.query = ""}) : _logs = logs, _keywords = keywords; - factory _$LogsAndKeywordsImpl.fromJson(Map json) => - _$$LogsAndKeywordsImplFromJson(json); - final List _logs; @override @JsonKey() @@ -1220,112 +1222,103 @@ class _$LogsAndKeywordsImpl implements _LogsAndKeywords { return EqualUnmodifiableListView(_keywords); } + @override + @JsonKey() + final String query; + @override String toString() { - return 'LogsAndKeywords(logs: $logs, keywords: $keywords)'; + return 'LogsState(logs: $logs, keywords: $keywords, query: $query)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$LogsAndKeywordsImpl && + other is _$LogsStateImpl && const DeepCollectionEquality().equals(other._logs, _logs) && - const DeepCollectionEquality().equals(other._keywords, _keywords)); + const DeepCollectionEquality().equals(other._keywords, _keywords) && + (identical(other.query, query) || other.query == query)); } - @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, const DeepCollectionEquality().hash(_logs), - const DeepCollectionEquality().hash(_keywords)); + const DeepCollectionEquality().hash(_keywords), + query); - /// Create a copy of LogsAndKeywords + /// Create a copy of LogsState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') - _$$LogsAndKeywordsImplCopyWith<_$LogsAndKeywordsImpl> get copyWith => - __$$LogsAndKeywordsImplCopyWithImpl<_$LogsAndKeywordsImpl>( - this, _$identity); - - @override - Map toJson() { - return _$$LogsAndKeywordsImplToJson( - this, - ); - } + _$$LogsStateImplCopyWith<_$LogsStateImpl> get copyWith => + __$$LogsStateImplCopyWithImpl<_$LogsStateImpl>(this, _$identity); } -abstract class _LogsAndKeywords implements LogsAndKeywords { - const factory _LogsAndKeywords( +abstract class _LogsState implements LogsState { + const factory _LogsState( {final List logs, - final List keywords}) = _$LogsAndKeywordsImpl; - - factory _LogsAndKeywords.fromJson(Map json) = - _$LogsAndKeywordsImpl.fromJson; + final List keywords, + final String query}) = _$LogsStateImpl; @override List get logs; @override List get keywords; + @override + String get query; - /// Create a copy of LogsAndKeywords + /// Create a copy of LogsState /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) - _$$LogsAndKeywordsImplCopyWith<_$LogsAndKeywordsImpl> get copyWith => + _$$LogsStateImplCopyWith<_$LogsStateImpl> get copyWith => throw _privateConstructorUsedError; } -ConnectionsAndKeywords _$ConnectionsAndKeywordsFromJson( - Map json) { - return _ConnectionsAndKeywords.fromJson(json); -} - /// @nodoc -mixin _$ConnectionsAndKeywords { +mixin _$ConnectionsState { List get connections => throw _privateConstructorUsedError; List get keywords => throw _privateConstructorUsedError; + String get query => throw _privateConstructorUsedError; - /// Serializes this ConnectionsAndKeywords to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ConnectionsAndKeywords + /// Create a copy of ConnectionsState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) - $ConnectionsAndKeywordsCopyWith get copyWith => + $ConnectionsStateCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $ConnectionsAndKeywordsCopyWith<$Res> { - factory $ConnectionsAndKeywordsCopyWith(ConnectionsAndKeywords value, - $Res Function(ConnectionsAndKeywords) then) = - _$ConnectionsAndKeywordsCopyWithImpl<$Res, ConnectionsAndKeywords>; +abstract class $ConnectionsStateCopyWith<$Res> { + factory $ConnectionsStateCopyWith( + ConnectionsState value, $Res Function(ConnectionsState) then) = + _$ConnectionsStateCopyWithImpl<$Res, ConnectionsState>; @useResult - $Res call({List connections, List keywords}); + $Res call( + {List connections, List keywords, String query}); } /// @nodoc -class _$ConnectionsAndKeywordsCopyWithImpl<$Res, - $Val extends ConnectionsAndKeywords> - implements $ConnectionsAndKeywordsCopyWith<$Res> { - _$ConnectionsAndKeywordsCopyWithImpl(this._value, this._then); +class _$ConnectionsStateCopyWithImpl<$Res, $Val extends ConnectionsState> + implements $ConnectionsStateCopyWith<$Res> { + _$ConnectionsStateCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; // ignore: unused_field final $Res Function($Val) _then; - /// Create a copy of ConnectionsAndKeywords + /// Create a copy of ConnectionsState /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? connections = null, Object? keywords = null, + Object? query = null, }) { return _then(_value.copyWith( connections: null == connections @@ -1336,41 +1329,44 @@ class _$ConnectionsAndKeywordsCopyWithImpl<$Res, ? _value.keywords : keywords // ignore: cast_nullable_to_non_nullable as List, + query: null == query + ? _value.query + : query // ignore: cast_nullable_to_non_nullable + as String, ) as $Val); } } /// @nodoc -abstract class _$$ConnectionsAndKeywordsImplCopyWith<$Res> - implements $ConnectionsAndKeywordsCopyWith<$Res> { - factory _$$ConnectionsAndKeywordsImplCopyWith( - _$ConnectionsAndKeywordsImpl value, - $Res Function(_$ConnectionsAndKeywordsImpl) then) = - __$$ConnectionsAndKeywordsImplCopyWithImpl<$Res>; +abstract class _$$ConnectionsStateImplCopyWith<$Res> + implements $ConnectionsStateCopyWith<$Res> { + factory _$$ConnectionsStateImplCopyWith(_$ConnectionsStateImpl value, + $Res Function(_$ConnectionsStateImpl) then) = + __$$ConnectionsStateImplCopyWithImpl<$Res>; @override @useResult - $Res call({List connections, List keywords}); + $Res call( + {List connections, List keywords, String query}); } /// @nodoc -class __$$ConnectionsAndKeywordsImplCopyWithImpl<$Res> - extends _$ConnectionsAndKeywordsCopyWithImpl<$Res, - _$ConnectionsAndKeywordsImpl> - implements _$$ConnectionsAndKeywordsImplCopyWith<$Res> { - __$$ConnectionsAndKeywordsImplCopyWithImpl( - _$ConnectionsAndKeywordsImpl _value, - $Res Function(_$ConnectionsAndKeywordsImpl) _then) +class __$$ConnectionsStateImplCopyWithImpl<$Res> + extends _$ConnectionsStateCopyWithImpl<$Res, _$ConnectionsStateImpl> + implements _$$ConnectionsStateImplCopyWith<$Res> { + __$$ConnectionsStateImplCopyWithImpl(_$ConnectionsStateImpl _value, + $Res Function(_$ConnectionsStateImpl) _then) : super(_value, _then); - /// Create a copy of ConnectionsAndKeywords + /// Create a copy of ConnectionsState /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? connections = null, Object? keywords = null, + Object? query = null, }) { - return _then(_$ConnectionsAndKeywordsImpl( + return _then(_$ConnectionsStateImpl( connections: null == connections ? _value._connections : connections // ignore: cast_nullable_to_non_nullable @@ -1379,22 +1375,24 @@ class __$$ConnectionsAndKeywordsImplCopyWithImpl<$Res> ? _value._keywords : keywords // ignore: cast_nullable_to_non_nullable as List, + query: null == query + ? _value.query + : query // ignore: cast_nullable_to_non_nullable + as String, )); } } /// @nodoc -@JsonSerializable() -class _$ConnectionsAndKeywordsImpl implements _ConnectionsAndKeywords { - const _$ConnectionsAndKeywordsImpl( + +class _$ConnectionsStateImpl implements _ConnectionsState { + const _$ConnectionsStateImpl( {final List connections = const [], - final List keywords = const []}) + final List keywords = const [], + this.query = ""}) : _connections = connections, _keywords = keywords; - factory _$ConnectionsAndKeywordsImpl.fromJson(Map json) => - _$$ConnectionsAndKeywordsImplFromJson(json); - final List _connections; @override @JsonKey() @@ -1413,64 +1411,62 @@ class _$ConnectionsAndKeywordsImpl implements _ConnectionsAndKeywords { return EqualUnmodifiableListView(_keywords); } + @override + @JsonKey() + final String query; + @override String toString() { - return 'ConnectionsAndKeywords(connections: $connections, keywords: $keywords)'; + return 'ConnectionsState(connections: $connections, keywords: $keywords, query: $query)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$ConnectionsAndKeywordsImpl && + other is _$ConnectionsStateImpl && const DeepCollectionEquality() .equals(other._connections, _connections) && - const DeepCollectionEquality().equals(other._keywords, _keywords)); + const DeepCollectionEquality().equals(other._keywords, _keywords) && + (identical(other.query, query) || other.query == query)); } - @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, const DeepCollectionEquality().hash(_connections), - const DeepCollectionEquality().hash(_keywords)); + const DeepCollectionEquality().hash(_keywords), + query); - /// Create a copy of ConnectionsAndKeywords + /// Create a copy of ConnectionsState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') - _$$ConnectionsAndKeywordsImplCopyWith<_$ConnectionsAndKeywordsImpl> - get copyWith => __$$ConnectionsAndKeywordsImplCopyWithImpl< - _$ConnectionsAndKeywordsImpl>(this, _$identity); - - @override - Map toJson() { - return _$$ConnectionsAndKeywordsImplToJson( - this, - ); - } + _$$ConnectionsStateImplCopyWith<_$ConnectionsStateImpl> get copyWith => + __$$ConnectionsStateImplCopyWithImpl<_$ConnectionsStateImpl>( + this, _$identity); } -abstract class _ConnectionsAndKeywords implements ConnectionsAndKeywords { - const factory _ConnectionsAndKeywords( +abstract class _ConnectionsState implements ConnectionsState { + const factory _ConnectionsState( {final List connections, - final List keywords}) = _$ConnectionsAndKeywordsImpl; - - factory _ConnectionsAndKeywords.fromJson(Map json) = - _$ConnectionsAndKeywordsImpl.fromJson; + final List keywords, + final String query}) = _$ConnectionsStateImpl; @override List get connections; @override List get keywords; + @override + String get query; - /// Create a copy of ConnectionsAndKeywords + /// Create a copy of ConnectionsState /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) - _$$ConnectionsAndKeywordsImplCopyWith<_$ConnectionsAndKeywordsImpl> - get copyWith => throw _privateConstructorUsedError; + _$$ConnectionsStateImplCopyWith<_$ConnectionsStateImpl> get copyWith => + throw _privateConstructorUsedError; } DAV _$DAVFromJson(Map json) { diff --git a/lib/models/generated/common.g.dart b/lib/models/generated/common.g.dart index 442f0ba0..7f671565 100644 --- a/lib/models/generated/common.g.dart +++ b/lib/models/generated/common.g.dart @@ -29,7 +29,7 @@ _$PackageImpl _$$PackageImplFromJson(Map json) => packageName: json['packageName'] as String, label: json['label'] as String, isSystem: json['isSystem'] as bool, - firstInstallTime: (json['firstInstallTime'] as num).toInt(), + lastUpdateTime: (json['lastUpdateTime'] as num).toInt(), ); Map _$$PackageImplToJson(_$PackageImpl instance) => @@ -37,7 +37,7 @@ Map _$$PackageImplToJson(_$PackageImpl instance) => 'packageName': instance.packageName, 'label': instance.label, 'isSystem': instance.isSystem, - 'firstInstallTime': instance.firstInstallTime, + 'lastUpdateTime': instance.lastUpdateTime, }; _$MetadataImpl _$$MetadataImplFromJson(Map json) => @@ -87,46 +87,6 @@ Map _$$ConnectionImplToJson(_$ConnectionImpl instance) => 'chains': instance.chains, }; -_$LogsAndKeywordsImpl _$$LogsAndKeywordsImplFromJson( - Map json) => - _$LogsAndKeywordsImpl( - logs: (json['logs'] as List?) - ?.map((e) => Log.fromJson(e as Map)) - .toList() ?? - const [], - keywords: (json['keywords'] as List?) - ?.map((e) => e as String) - .toList() ?? - const [], - ); - -Map _$$LogsAndKeywordsImplToJson( - _$LogsAndKeywordsImpl instance) => - { - 'logs': instance.logs, - 'keywords': instance.keywords, - }; - -_$ConnectionsAndKeywordsImpl _$$ConnectionsAndKeywordsImplFromJson( - Map json) => - _$ConnectionsAndKeywordsImpl( - connections: (json['connections'] as List?) - ?.map((e) => Connection.fromJson(e as Map)) - .toList() ?? - const [], - keywords: (json['keywords'] as List?) - ?.map((e) => e as String) - .toList() ?? - const [], - ); - -Map _$$ConnectionsAndKeywordsImplToJson( - _$ConnectionsAndKeywordsImpl instance) => - { - 'connections': instance.connections, - 'keywords': instance.keywords, - }; - _$DAVImpl _$$DAVImplFromJson(Map json) => _$DAVImpl( uri: json['uri'] as String, user: json['user'] as String, diff --git a/lib/models/generated/widget.freezed.dart b/lib/models/generated/widget.freezed.dart index c6a930f5..e984f420 100644 --- a/lib/models/generated/widget.freezed.dart +++ b/lib/models/generated/widget.freezed.dart @@ -310,3 +310,188 @@ abstract class _CommonMessage implements CommonMessage { _$$CommonMessageImplCopyWith<_$CommonMessageImpl> get copyWith => throw _privateConstructorUsedError; } + +/// @nodoc +mixin _$CommonAppBarState { + List get actions => throw _privateConstructorUsedError; + dynamic Function(String)? get onSearch => throw _privateConstructorUsedError; + bool get searching => throw _privateConstructorUsedError; + + /// Create a copy of CommonAppBarState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $CommonAppBarStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CommonAppBarStateCopyWith<$Res> { + factory $CommonAppBarStateCopyWith( + CommonAppBarState value, $Res Function(CommonAppBarState) then) = + _$CommonAppBarStateCopyWithImpl<$Res, CommonAppBarState>; + @useResult + $Res call( + {List actions, + dynamic Function(String)? onSearch, + bool searching}); +} + +/// @nodoc +class _$CommonAppBarStateCopyWithImpl<$Res, $Val extends CommonAppBarState> + implements $CommonAppBarStateCopyWith<$Res> { + _$CommonAppBarStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of CommonAppBarState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? actions = null, + Object? onSearch = freezed, + Object? searching = null, + }) { + return _then(_value.copyWith( + actions: null == actions + ? _value.actions + : actions // ignore: cast_nullable_to_non_nullable + as List, + onSearch: freezed == onSearch + ? _value.onSearch + : onSearch // ignore: cast_nullable_to_non_nullable + as dynamic Function(String)?, + searching: null == searching + ? _value.searching + : searching // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$CommonAppBarStateImplCopyWith<$Res> + implements $CommonAppBarStateCopyWith<$Res> { + factory _$$CommonAppBarStateImplCopyWith(_$CommonAppBarStateImpl value, + $Res Function(_$CommonAppBarStateImpl) then) = + __$$CommonAppBarStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {List actions, + dynamic Function(String)? onSearch, + bool searching}); +} + +/// @nodoc +class __$$CommonAppBarStateImplCopyWithImpl<$Res> + extends _$CommonAppBarStateCopyWithImpl<$Res, _$CommonAppBarStateImpl> + implements _$$CommonAppBarStateImplCopyWith<$Res> { + __$$CommonAppBarStateImplCopyWithImpl(_$CommonAppBarStateImpl _value, + $Res Function(_$CommonAppBarStateImpl) _then) + : super(_value, _then); + + /// Create a copy of CommonAppBarState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? actions = null, + Object? onSearch = freezed, + Object? searching = null, + }) { + return _then(_$CommonAppBarStateImpl( + actions: null == actions + ? _value._actions + : actions // ignore: cast_nullable_to_non_nullable + as List, + onSearch: freezed == onSearch + ? _value.onSearch + : onSearch // ignore: cast_nullable_to_non_nullable + as dynamic Function(String)?, + searching: null == searching + ? _value.searching + : searching // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$CommonAppBarStateImpl implements _CommonAppBarState { + const _$CommonAppBarStateImpl( + {final List actions = const [], + this.onSearch, + this.searching = false}) + : _actions = actions; + + final List _actions; + @override + @JsonKey() + List get actions { + if (_actions is EqualUnmodifiableListView) return _actions; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_actions); + } + + @override + final dynamic Function(String)? onSearch; + @override + @JsonKey() + final bool searching; + + @override + String toString() { + return 'CommonAppBarState(actions: $actions, onSearch: $onSearch, searching: $searching)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$CommonAppBarStateImpl && + const DeepCollectionEquality().equals(other._actions, _actions) && + (identical(other.onSearch, onSearch) || + other.onSearch == onSearch) && + (identical(other.searching, searching) || + other.searching == searching)); + } + + @override + int get hashCode => Object.hash(runtimeType, + const DeepCollectionEquality().hash(_actions), onSearch, searching); + + /// Create a copy of CommonAppBarState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$CommonAppBarStateImplCopyWith<_$CommonAppBarStateImpl> get copyWith => + __$$CommonAppBarStateImplCopyWithImpl<_$CommonAppBarStateImpl>( + this, _$identity); +} + +abstract class _CommonAppBarState implements CommonAppBarState { + const factory _CommonAppBarState( + {final List actions, + final dynamic Function(String)? onSearch, + final bool searching}) = _$CommonAppBarStateImpl; + + @override + List get actions; + @override + dynamic Function(String)? get onSearch; + @override + bool get searching; + + /// Create a copy of CommonAppBarState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$CommonAppBarStateImplCopyWith<_$CommonAppBarStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/selector.dart b/lib/models/selector.dart index e28ee121..c928c2a8 100644 --- a/lib/models/selector.dart +++ b/lib/models/selector.dart @@ -164,7 +164,7 @@ extension PackageListSelectorStateExt on PackageListSelectorState { other.getPinyin(b.label), ), AccessSortType.time => - a.firstInstallTime.compareTo(b.firstInstallTime), + b.lastUpdateTime.compareTo(a.lastUpdateTime), }; }, ).sorted( diff --git a/lib/models/widget.dart b/lib/models/widget.dart index 49b47a5c..52dc23ab 100644 --- a/lib/models/widget.dart +++ b/lib/models/widget.dart @@ -1,3 +1,4 @@ +import 'package:flutter/widgets.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'generated/widget.freezed.dart'; @@ -17,3 +18,12 @@ class CommonMessage with _$CommonMessage { @Default(Duration(seconds: 3)) Duration duration, }) = _CommonMessage; } + +@freezed +class CommonAppBarState with _$CommonAppBarState { + const factory CommonAppBarState({ + @Default([]) List actions, + Function(String)? onSearch, + @Default(false) bool searching, + }) = _CommonAppBarState; +} diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 52032f72..aeafb589 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -21,7 +21,10 @@ class HomePage extends StatelessWidget { final currentIndex = index == -1 ? 0 : index; if (globalState.pageController != null) { WidgetsBinding.instance.addPostFrameCallback((_) { - globalState.appController.toPage(currentIndex, hasAnimate: true); + globalState.appController.toPage( + currentIndex, + hasAnimate: true, + ); }); } else { globalState.pageController = PageController( @@ -152,71 +155,69 @@ class CommonNavigationBar extends StatelessWidget { } return Material( color: context.colorScheme.surfaceContainer, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 16, - ), - child: Column( - children: [ - Expanded( - child: SingleChildScrollView( - child: IntrinsicHeight( - child: Selector( - selector: (_, config) => config.appSetting.showLabel, - builder: (_, showLabel, __) { - return NavigationRail( - backgroundColor: context.colorScheme.surfaceContainer, - selectedIconTheme: IconThemeData( - color: context.colorScheme.onSurfaceVariant, - ), - unselectedIconTheme: IconThemeData( - color: context.colorScheme.onSurfaceVariant, - ), - selectedLabelTextStyle: - context.textTheme.labelLarge!.copyWith( - color: context.colorScheme.onSurface, - ), - unselectedLabelTextStyle: - context.textTheme.labelLarge!.copyWith( - color: context.colorScheme.onSurface, - ), - destinations: navigationItems - .map( - (e) => NavigationRailDestination( - icon: e.icon, - label: Text( - Intl.message(e.label), - ), + child: Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: IntrinsicHeight( + child: Selector( + selector: (_, config) => config.appSetting.showLabel, + builder: (_, showLabel, __) { + return NavigationRail( + backgroundColor: context.colorScheme.surfaceContainer, + selectedIconTheme: IconThemeData( + color: context.colorScheme.onSurfaceVariant, + ), + unselectedIconTheme: IconThemeData( + color: context.colorScheme.onSurfaceVariant, + ), + selectedLabelTextStyle: + context.textTheme.labelLarge!.copyWith( + color: context.colorScheme.onSurface, + ), + unselectedLabelTextStyle: + context.textTheme.labelLarge!.copyWith( + color: context.colorScheme.onSurface, + ), + destinations: navigationItems + .map( + (e) => NavigationRailDestination( + icon: e.icon, + label: Text( + Intl.message(e.label), ), - ) - .toList(), - onDestinationSelected: globalState.appController.toPage, - extended: false, - selectedIndex: currentIndex, - labelType: showLabel - ? NavigationRailLabelType.all - : NavigationRailLabelType.none, - ); - }, - ), + ), + ) + .toList(), + onDestinationSelected: globalState.appController.toPage, + extended: false, + selectedIndex: currentIndex, + labelType: showLabel + ? NavigationRailLabelType.all + : NavigationRailLabelType.none, + ); + }, ), ), ), - const SizedBox( - height: 16, - ), - IconButton( - onPressed: () { - final config = globalState.appController.config; - final appSetting = config.appSetting; - config.appSetting = appSetting.copyWith( - showLabel: !appSetting.showLabel, - ); - }, - icon: const Icon(Icons.menu), - ) - ], - ), + ), + const SizedBox( + height: 16, + ), + IconButton( + onPressed: () { + final config = globalState.appController.config; + final appSetting = config.appSetting; + config.appSetting = appSetting.copyWith( + showLabel: !appSetting.showLabel, + ); + }, + icon: const Icon(Icons.menu), + ), + const SizedBox( + height: 16, + ), + ], ), ); } diff --git a/lib/widgets/chip.dart b/lib/widgets/chip.dart index 6046c27d..eac7ab71 100644 --- a/lib/widgets/chip.dart +++ b/lib/widgets/chip.dart @@ -20,10 +20,11 @@ class CommonChip extends StatelessWidget { if (type == ChipType.delete) { return Chip( avatar: avatar, - labelPadding:const EdgeInsets.symmetric( + labelPadding: const EdgeInsets.symmetric( vertical: 0, horizontal: 4, ), + clipBehavior: Clip.antiAlias, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, onDeleted: onPressed ?? () {}, side: @@ -35,7 +36,8 @@ class CommonChip extends StatelessWidget { return ActionChip( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, avatar: avatar, - labelPadding:const EdgeInsets.symmetric( + clipBehavior: Clip.antiAlias, + labelPadding: const EdgeInsets.symmetric( vertical: 0, horizontal: 4, ), diff --git a/lib/widgets/connection_item.dart b/lib/widgets/connection_item.dart deleted file mode 100644 index c9bf8ef3..00000000 --- a/lib/widgets/connection_item.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'dart:io'; - -import 'package:fl_clash/common/common.dart'; -import 'package:fl_clash/enum/enum.dart'; -import 'package:fl_clash/models/models.dart'; -import 'package:fl_clash/plugins/app.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import 'chip.dart'; -import 'list.dart'; - -class ConnectionItem extends StatelessWidget { - final Connection connection; - final Function(String)? onClick; - final Widget? trailing; - - const ConnectionItem({ - super.key, - required this.connection, - this.onClick, - this.trailing, - }); - - Future _getPackageIcon(Connection connection) async { - return await app?.getPackageIcon(connection.metadata.process); - } - - String _getRequestText(Metadata metadata) { - var text = "${metadata.network}://"; - final ips = [ - metadata.host, - metadata.destinationIP, - ].where((ip) => ip.isNotEmpty); - text += ips.join("/"); - text += ":${metadata.destinationPort}"; - return text; - } - - String _getSourceText(Connection connection) { - final metadata = connection.metadata; - if (metadata.process.isEmpty) { - return connection.start.lastUpdateTimeDesc; - } - return "${metadata.process} · ${connection.start.lastUpdateTimeDesc}"; - } - - @override - Widget build(BuildContext context) { - if (!Platform.isAndroid) { - return ListItem( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 4, - ), - tileTitleAlignment: ListTileTitleAlignment.titleHeight, - title: Text( - _getRequestText(connection.metadata), - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 8, - ), - Text( - _getSourceText(connection), - ), - const SizedBox( - height: 8, - ), - Wrap( - runSpacing: 6, - spacing: 6, - children: [ - for (final chain in connection.chains) - CommonChip( - label: chain, - onPressed: () { - if (onClick == null) return; - onClick!(chain); - }, - ), - ], - ), - ], - ), - trailing: trailing, - ); - } - return Selector( - selector: (_, clashConfig) => - clashConfig.findProcessMode == FindProcessMode.always, - builder: (_, value, child) { - return ListItem( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 4, - ), - tileTitleAlignment: ListTileTitleAlignment.titleHeight, - leading: value - ? GestureDetector( - onTap: () { - if (onClick == null) return; - final process = connection.metadata.process; - if(process.isEmpty) return; - onClick!(process); - }, - child: Container( - margin: const EdgeInsets.only(top: 4), - width: 48, - height: 48, - child: FutureBuilder( - future: _getPackageIcon(connection), - builder: (_, snapshot) { - if (!snapshot.hasData && snapshot.data == null) { - return Container(); - } else { - return Image( - image: snapshot.data!, - gaplessPlayback: true, - width: 48, - height: 48, - ); - } - }, - ), - ), - ) - : null, - title: Text( - _getRequestText(connection.metadata), - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 8, - ), - Text( - _getSourceText(connection), - ), - const SizedBox( - height: 8, - ), - Wrap( - runSpacing: 6, - spacing: 6, - children: [ - for (final chain in connection.chains) - CommonChip( - label: chain, - onPressed: () { - if (onClick == null) return; - onClick!(chain); - }, - ), - ], - ), - ], - ), - trailing: trailing, - ); - }, - ); - } -} diff --git a/lib/widgets/popup.dart b/lib/widgets/popup.dart index 4a576cb8..e56f8279 100644 --- a/lib/widgets/popup.dart +++ b/lib/widgets/popup.dart @@ -178,10 +178,6 @@ class CommonPopupMenu extends StatelessWidget { ? context.colorScheme.error : context.colorScheme.onSurfaceVariant; return InkWell( - hoverColor: - isDanger ? context.colorScheme.errorContainer.withOpacity(0.3) : null, - splashColor: - isDanger ? context.colorScheme.errorContainer.withOpacity(0.4) : null, onTap: () { Navigator.of(context).pop(); item.onPressed(); diff --git a/lib/widgets/scaffold.dart b/lib/widgets/scaffold.dart index e3a0f7f8..6dac4252 100644 --- a/lib/widgets/scaffold.dart +++ b/lib/widgets/scaffold.dart @@ -1,9 +1,13 @@ import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/state.dart'; import 'package:fl_clash/widgets/fade_box.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import '../enum/enum.dart'; +import 'chip.dart'; + class CommonScaffold extends StatefulWidget { final Widget body; final Widget? bottomNavigationBar; @@ -47,14 +51,32 @@ class CommonScaffold extends StatefulWidget { } class CommonScaffoldState extends State { - final ValueNotifier> _actions = ValueNotifier([]); + final ValueNotifier _appBarState = + ValueNotifier(CommonAppBarState()); final ValueNotifier _floatingActionButton = ValueNotifier(null); + final ValueNotifier> _keywordsNotifier = ValueNotifier([]); final ValueNotifier _loading = ValueNotifier(false); + final _textController = TextEditingController(); + + Function(List)? _onKeywordsUpdate; + + Widget? get _sideNavigationBar => widget.sideNavigationBar; + set actions(List actions) { - if (_actions.value != actions) { - _actions.value = actions; - } + _appBarState.value = _appBarState.value.copyWith(actions: actions); + } + + set onSearch(Function(String)? onSearch) { + _appBarState.value = _appBarState.value.copyWith(onSearch: onSearch); + } + + set onKeywordsUpdate(Function(List)? onKeywordsUpdate) { + _onKeywordsUpdate = onKeywordsUpdate; + } + + set _searching(bool searching) { + _appBarState.value = _appBarState.value.copyWith(searching: searching); } set floatingActionButton(Widget? floatingActionButton) { @@ -63,6 +85,28 @@ class CommonScaffoldState extends State { } } + ThemeData _appBarTheme(BuildContext context) { + final ThemeData theme = Theme.of(context); + final ColorScheme colorScheme = theme.colorScheme; + return theme.copyWith( + appBarTheme: AppBarTheme( + systemOverlayStyle: colorScheme.brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, + backgroundColor: colorScheme.brightness == Brightness.dark + ? Colors.grey[900] + : Colors.white, + iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey), + titleTextStyle: theme.textTheme.titleLarge, + toolbarTextStyle: theme.textTheme.bodyMedium, + ), + inputDecorationTheme: InputDecorationTheme( + hintStyle: theme.inputDecorationTheme.hintStyle, + border: InputBorder.none, + ), + ); + } + Future loadingRun( Future Function() futureFunction, { String? title, @@ -84,9 +128,31 @@ class CommonScaffoldState extends State { } } + _handleClearInput() { + _textController.text = ""; + + if (_appBarState.value.onSearch != null) { + _appBarState.value.onSearch!(""); + } + } + + _handleClear() { + if (_textController.text.isNotEmpty) { + _handleClearInput(); + return; + } + _searching = false; + } + + _handleExitSearching() { + _handleClearInput(); + _searching = false; + } + @override void dispose() { - _actions.dispose(); + _appBarState.dispose(); + _textController.dispose(); _floatingActionButton.dispose(); super.dispose(); } @@ -95,59 +161,172 @@ class CommonScaffoldState extends State { void didUpdateWidget(CommonScaffold oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.title != widget.title) { - _actions.value = []; + _appBarState.value = CommonAppBarState(); _floatingActionButton.value = null; + _textController.text = ""; + _keywordsNotifier.value = []; + _onKeywordsUpdate = null; } } - Widget? get _sideNavigationBar => widget.sideNavigationBar; + addKeyword(String keyword) { + final isContains = _keywordsNotifier.value.contains(keyword); + if (isContains) return; + final keywords = List.from(_keywordsNotifier.value)..add(keyword); + _keywordsNotifier.value = keywords; + } - Widget get body => SafeArea(child: widget.body); + _deleteKeyword(String keyword) { + final isContains = _keywordsNotifier.value.contains(keyword); + if (!isContains) return; + final keywords = List.from(_keywordsNotifier.value) + ..remove(keyword); + _keywordsNotifier.value = keywords; + } @override Widget build(BuildContext context) { + final body = SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ValueListenableBuilder( + valueListenable: _keywordsNotifier, + builder: (_, keywords, __) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_onKeywordsUpdate != null) { + _onKeywordsUpdate!(keywords); + } + }); + if (keywords.isEmpty) { + return SizedBox(); + } + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 16, + ), + child: Wrap( + runSpacing: 8, + spacing: 8, + children: [ + for (final keyword in keywords) + CommonChip( + label: keyword, + type: ChipType.delete, + onPressed: () { + _deleteKeyword(keyword); + }, + ), + ], + ), + ); + }, + ), + Expanded( + child: widget.body, + ), + ], + ), + ); final scaffold = Scaffold( appBar: PreferredSize( preferredSize: const Size.fromHeight(kToolbarHeight), child: Stack( alignment: Alignment.bottomCenter, children: [ - ValueListenableBuilder>( - valueListenable: _actions, - builder: (_, actions, __) { - final realActions = - actions.isNotEmpty ? actions : widget.actions ?? []; - return AppBar( + ValueListenableBuilder( + valueListenable: _appBarState, + builder: (_, state, __) { + final realActions = [ + if (state.onSearch != null) + IconButton( + onPressed: () { + _searching = true; + }, + icon: Icon(Icons.search), + ), + ...state.actions.isNotEmpty + ? state.actions + : widget.actions ?? [] + ]; + final appBar = AppBar( centerTitle: false, systemOverlayStyle: SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: - Theme.of(context).brightness == Brightness.dark - ? Brightness.light - : Brightness.dark, + Theme.of(context).brightness == Brightness.dark + ? Brightness.light + : Brightness.dark, systemNavigationBarIconBrightness: - Theme.of(context).brightness == Brightness.dark - ? Brightness.light - : Brightness.dark, + Theme.of(context).brightness == Brightness.dark + ? Brightness.light + : Brightness.dark, systemNavigationBarColor: widget.bottomNavigationBar != null ? context.colorScheme.surfaceContainer : context.colorScheme.surface, systemNavigationBarDividerColor: Colors.transparent, ), automaticallyImplyLeading: widget.automaticallyImplyLeading, - leading: widget.leading, - title: Text(widget.title), + leading: state.searching + ? IconButton( + onPressed: _handleExitSearching, + icon: Icon(Icons.arrow_back), + ) + : widget.leading, + title: state.searching + ? TextField( + autofocus: true, + controller: _textController, + style: context.textTheme.titleLarge, + onChanged: (value) { + if (state.onSearch != null) { + state.onSearch!(value); + } + }, + decoration: InputDecoration( + hintText: appLocalizations.search, + ), + ) + : Text(widget.title), actions: [ - ...realActions.separated( - SizedBox( - width: 4, + if (state.searching) + IconButton( + onPressed: _handleClear, + icon: Icon(Icons.close), + ) + else + ...realActions.separated( + SizedBox( + width: 4, + ), ), - ), SizedBox( width: 8, ) ], ); + return FadeBox( + child: state.searching + ? Theme( + data: _appBarTheme(context), + child: PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, __) { + if (didPop) { + return; + } + if (state.searching) { + _handleExitSearching(); + return; + } + Navigator.of(context).pop(); + }, + child: appBar, + ), + ) + : appBar, + ); }, ), ValueListenableBuilder( @@ -179,12 +358,7 @@ class CommonScaffoldState extends State { _sideNavigationBar!, Expanded( flex: 1, - child: Material( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 8), - child: scaffold, - ), - ), + child: scaffold, ), ], ) diff --git a/lib/widgets/scroll.dart b/lib/widgets/scroll.dart new file mode 100644 index 00000000..fc6abc8d --- /dev/null +++ b/lib/widgets/scroll.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class CommonScrollBar extends StatelessWidget { + final ScrollController controller; + final Widget child; + + const CommonScrollBar({ + super.key, + required this.child, + required this.controller, + }); + + @override + Widget build(BuildContext context) { + return Scrollbar( + controller: controller, + thumbVisibility: true, + trackVisibility: true, + thickness: 8, + radius: const Radius.circular(8), + interactive: true, + child: child, + ); + } +} diff --git a/lib/widgets/view.dart b/lib/widgets/view.dart new file mode 100644 index 00000000..b370d1f0 --- /dev/null +++ b/lib/widgets/view.dart @@ -0,0 +1,20 @@ +import 'package:flutter/cupertino.dart'; + +class CommonView extends StatefulWidget { + final List actions; + + const CommonView({ + super.key, + required this.actions, + }); + + @override + State createState() => _CommonViewState(); +} + +class _CommonViewState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index 7c604e4d..05df3896 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -4,7 +4,6 @@ export 'builder.dart'; export 'card.dart'; export 'chip.dart'; export 'color_scheme_box.dart'; -export 'connection_item.dart'; export 'disabled_mask.dart'; export 'fade_box.dart'; export 'float_layout.dart'; @@ -27,3 +26,4 @@ export 'super_grid.dart'; export 'donut_chart.dart'; export 'activate_box.dart'; export 'wave.dart'; +export 'scroll.dart'; diff --git a/plugins/proxy/lib/proxy.dart b/plugins/proxy/lib/proxy.dart index 72a155cf..03abb909 100644 --- a/plugins/proxy/lib/proxy.dart +++ b/plugins/proxy/lib/proxy.dart @@ -210,7 +210,7 @@ class Proxy extends ProxyPlatform { [ "-setproxybypassdomains", dev, - bypassDomain.join(" "), + bypassDomain.join(","), ], ), ]); diff --git a/pubspec.lock b/pubspec.lock index cc98b114..b3e4813e 100755 --- a/pubspec.lock +++ b/pubspec.lock @@ -166,14 +166,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - charcode: - dependency: transitive - description: - name: charcode - sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a - url: "https://pub.dev" - source: hosted - version: "1.4.0" checked_yaml: dependency: transitive description: @@ -570,14 +562,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" - image: - dependency: "direct main" - description: - name: image - sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d - url: "https://pub.dev" - source: hosted - version: "4.3.0" image_picker: dependency: "direct main" description: @@ -1061,10 +1045,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82" + sha256: "688ee90fbfb6989c980254a56cb26ebe9bb30a3a2dff439a78894211f73de67a" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.5.1" shared_preferences_android: dependency: transitive description: @@ -1501,14 +1485,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" - zxing2: - dependency: "direct main" - description: - name: zxing2 - sha256: "6cf995abd3c86f01ba882968dedffa7bc130185e382f2300239d2e857fc7912c" - url: "https://pub.dev" - source: hosted - version: "0.2.3" sdks: dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 412ae918..ff9eec25 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: fl_clash description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free. publish_to: 'none' -version: 0.8.74+202502031 +version: 0.8.75+202502091 environment: sdk: '>=3.1.0 <4.0.0' @@ -13,7 +13,7 @@ dependencies: intl: ^0.19.0 path_provider: ^2.1.0 path: ^1.9.0 - shared_preferences: ^2.2.0 + shared_preferences: ^2.5.1 provider: ^6.0.5 window_manager: ^0.4.3 dynamic_color: ^1.7.0 @@ -35,8 +35,6 @@ dependencies: url_launcher: ^6.2.6 freezed_annotation: ^2.4.1 image_picker: ^1.1.2 - zxing2: ^0.2.3 - image: ^4.1.7 webdav_client: ^1.2.2 dio: ^5.4.3+1 win32: ^5.5.1