Fix macos performance issue

Support custom global-ua

Update core

Optimize some details
This commit is contained in:
chen08209
2026-05-27 15:16:51 +08:00
parent 5179d55dfb
commit 7e7f1f89f3
192 changed files with 4904 additions and 2910 deletions
+28
View File
@@ -0,0 +1,28 @@
# Agent Configuration Model
Use the smallest durable surface that matches the job.
## Surfaces
- `AGENTS.md`: auto-loaded repository entry point. Keep it small and reserve it for always-on rules, routing, and high-priority expectations.
- `.agents/*.md`: human- and agent-readable reference docs linked from `AGENTS.md`. Use these for detailed project context, commands, architecture, and conventions.
- `.agents/skills/*/SKILL.md`: repo-scoped Codex skills. Use these for repeatable workflows that should be discoverable by name and description.
- `.codex/config.toml`: trusted project Codex settings such as MCP, hooks, sandbox, approval, or model defaults.
- `.codex/rules/*.rules`: command permission rules for running commands outside the sandbox. Do not mix these with coding conventions in `.agents/rules.md`.
- `.codex/hooks.json` or `.codex/config.toml` hooks: lifecycle automation such as prompt checks, command validation, or final verification gates.
## Placement Rules
- Put stable team conventions in `AGENTS.md` only when every task must see them without opening another file.
- Put detailed explanations in `.agents/*.md` and link them from `AGENTS.md`.
- Put reusable task workflows in `.agents/skills/<skill-name>/SKILL.md`.
- Put mechanical enforcement in linters, tests, hooks, or `.codex/rules`; do not rely on prose when tooling can enforce the rule.
- Keep user-specific preferences out of the repository. They belong in user-level Codex configuration or user-level skills.
## Skill Authoring Rules
- Use lowercase hyphenated names.
- Start descriptions with `Use when...`.
- Describe trigger conditions in the description, not the workflow.
- Keep `SKILL.md` lean; link to `.agents/*.md` for large reference material.
- Add scripts only when deterministic behavior is needed repeatedly.
+156
View File
@@ -0,0 +1,156 @@
# Architecture
## Core Integration
The Go proxy core in `core/` operates in two modes.
Android lib mode:
- Go core is compiled as a C shared library, `libclash.so`, through `go build -buildmode=c-shared` with CGO.
- Flutter calls it via FFI through the `service` plugin.
- Dart-side implementation: `lib/core/lib.dart` (`CoreLib`).
Desktop core mode:
- Go core runs as a separate process with `CGO_ENABLED=0`.
- Flutter communicates via JSON over socket, using a Unix socket on macOS/Linux and TCP on Windows.
- Dart-side implementation: `lib/core/service.dart` (`CoreService`).
`lib/core/controller.dart` (`CoreController`) selects the implementation based on platform. `lib/core/interface.dart` defines the shared `CoreHandlerInterface`.
Key Go core files:
- `core/hub.go`: handler functions.
- `core/action.go`: dispatch.
- `core/lib.go`: CGO exports.
- `core/server.go`: socket server.
## State Management
Provider files in `lib/providers/`:
- `app.dart`: runtime/UI state, logs, traffic, delays, loading, navigation.
- `config.dart`: persistent config providers, app settings, theme, VPN, proxy style.
- `state.dart`: derived/computed providers, navigation, proxy, tray, color scheme.
- `action.dart`: business logic notifiers, setup, backup, core lifecycle, proxy selection.
- `database.dart`: Drift database provider wrappers.
`globalState` in `lib/state.dart` is a singleton holding app lifecycle, timers, theme, and start/stop state. Providers are generated into `lib/providers/generated/`.
## Database
The app uses Drift/SQLite in `lib/database/`. Current schema version is 2.
Tables:
- `Profiles`
- `Scripts`
- `Rules`
- `ProfileRuleLinks` (`profile_rule_mapping`)
- `ProxyGroups`
- `IconRecords` (`icon_records`)
Rule scenes distinguish global added rules, profile added rules, profile custom rules, and disabled links. Rule and proxy-group ordering use fractional indexing.
Generated Drift output lives in `lib/database/generated/database.g.dart`. After schema changes, run code generation and add or update focused database tests under `test/database/` when converter or migration behavior changes.
## Manager Stack
Managers are nested `InheritedWidget`/`StatefulWidget` components in `lib/application.dart`:
```text
AppEnvManager > StatusManager > ThemeManager
> [Desktop: WindowManager > TrayManager > HotKeyManager > ProxyManager]
> ConnectivityManager > CoreManager > AppStateManager
> [Mobile: AndroidManager > VpnManager | Desktop: WindowHeaderContainer]
```
Each manager in `lib/manager/` handles a specific platform concern. Desktop-only managers are conditionally inserted.
## Core Controller and Actions
`lib/core/controller.dart` (`CoreController`) is a singleton facade over `CoreHandlerInterface`. Public methods delegate to the platform-specific interface, either Android FFI or desktop socket. It has an `@visibleForTesting` constructor and `resetInstance()` for test injection.
Business logic lives in Riverpod notifier classes in `lib/providers/action.dart`:
- `CommonAction`: update check and common UI operations.
- `SetupAction`: config setup and TUN management.
- `BackupAction`: backup/restore with WebDAV sync.
- `CoreAction`: core lifecycle, init, connect, restart, shutdown.
- `SystemAction`: system integration, tray, exit, brightness.
- `StoreAction`: profile storage operations.
- `ThemeAction`: theme state updates.
- `ProxiesAction`: group management and proxy selection.
- `ProfilesAction`: profile CRUD, auto-update, import.
## Platform Managers
Desktop:
- `WindowManager`
- `TrayManager`
- `HotKeyManager`
- `ProxyManager`
Mobile:
- `AndroidManager`
- `TileManager`
- `VpnManager`
Shared:
- `ConnectivityManager`
- `CoreManager`
- `AppStateManager`
- `StatusManager`
- `ThemeManager`
## Build System
`setup.dart` is the release build orchestrator:
1. On Windows, pre-builds Go core via `dart run build_tool windows` and reads `core_sha256.json`.
2. Writes `env.json` (`APP_ENV`).
3. Passes SHA256 as `--dart-define=CORE_SHA256=$val`, embedded at compile time for Windows.
4. Activates `flutter_distributor` for packaging.
Go core building is handled by `build_tool`, a standalone Dart CLI in `plugins/setup/buildkit/build_tool/`.
Platform build hooks inside `flutter build` trigger `build_tool` automatically:
- macOS: podspec script phase, `build_pod.sh`, `build_tool macos`.
- Linux: CMake include, `buildkit/cmake/buildkit.cmake`, `build_tool linux`.
- Windows: CMake include, `buildkit/cmake/buildkit.cmake`, `build_tool windows`. Debug passes `--dev` via `CMAKE_BUILD_TYPE`.
- Android: Gradle include, `buildkit/gradle/plugin.gradle`, `build_tool android`.
Windows helper auth:
- Release: Core SHA256 is embedded in both the Flutter app and the Rust helper. The app pings the helper and verifies the token matches.
- Debug: The Rust helper skips token verification when built in debug mode, so `flutter run` works without the SHA256 flow.
`plugins/setup/` is an FFI plugin that exists only as a build harness. It carries no Dart API, only platform build hooks that trigger Go compilation. Windows builds also compile a Rust helper in `services/helper/` through `RustBuilder`.
Build configuration defaults live in `build_tool/lib/src/options.dart` and can be overridden via `build_config.yaml`.
Architecture detection is automatic. The `--description` flag passed to `flutter_distributor` adds arch suffixes to artifact names, such as `FlClash-0.8.93-macos-arm64.dmg`.
## Local Plugins
- `setup`: build harness FFI plugin.
- `proxy`: system proxy configuration.
- `rust_api`: Flutter Rust Bridge FFI plugin.
- `tray_manager`: system tray fork/customization.
- `wifi_ssid`: Wi-Fi SSID detection.
- `window_ext`: window extensions.
- `flutter_distributor`: app packaging/distribution.
## Rust Helper Service
`services/helper/` is a Windows-only privileged helper for starting the core as admin and managing TUN. It is built with:
```bash
cargo build --release --features windows-service
```
It uses token-based auth with the Flutter app.
+106
View File
@@ -0,0 +1,106 @@
# Commands
## Building
Update submodules first. The ClashMeta Go core lives in `core/Clash.Meta/`.
```bash
git submodule update --init --recursive
```
Full package build, including Go core, Flutter, and packaging, runs through `setup.dart`:
```bash
dart setup.dart macos
dart setup.dart linux
dart setup.dart windows
dart setup.dart android
```
Build only the Go core and skip Flutter packaging:
```bash
make core-macos
make core-linux
make core-windows
make core-android
```
Pass `ARCH` or `TARGET_PLATFORM` through `make` when needed, for example:
```bash
make core-macos ARCH=arm64
make core-android TARGET_PLATFORM=android-arm64
```
The Makefile wraps `plugins/setup/buildkit/run_build_tool.sh`; prefer the `make` entry points unless debugging the build tool itself.
## Flutter Development
The project is pinned with FVM.
```bash
fvm flutter pub get
fvm flutter run
fvm flutter test
```
Plain Flutter also works when the global SDK matches project constraints:
```bash
flutter pub get
flutter run
flutter test
```
Use `flutter test`, not `dart test`, because models pull in Flutter types.
## Code Generation
Run code generation after modifying models, providers, or database schema:
```bash
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch
```
Code generation covers:
- Riverpod providers through `riverpod_generator`.
- Models through `freezed` and `json_serializable`.
- Database tables through `drift_dev`.
Generated output paths, configured in `build.yaml`:
- `lib/models/generated/*.g.dart`, `*.freezed.dart`.
- `lib/providers/generated/*.g.dart`.
- `lib/database/generated/*.g.dart`.
## Testing
Tests use `package:test/test.dart` for pure Dart logic and `flutter_test` for provider and widget tests. `mocktail` is the mocking framework.
```bash
flutter test test/models/
flutter test test/core/
flutter test test/providers/
flutter test test/common/
flutter test test/database/
flutter test test/widgets/
flutter test test/setup_test.dart
flutter test plugins/proxy/test/proxy_test.dart
```
Root `flutter test` only discovers the root package's `test/` directory by default. Include bundled plugin Dart tests by passing paths explicitly, or run `flutter test` from that plugin package directory. Native plugin tests under platform folders are not run by `flutter test`.
## Verify
CI runs these in order:
```bash
flutter pub get
flutter analyze --no-fatal-infos
flutter test --reporter expanded
```
Run `flutter analyze` locally before committing when practical.
+28
View File
@@ -0,0 +1,28 @@
# Project Context
FlClash is a multi-platform proxy client based on ClashMeta (mihomo), built with Flutter. It supports Android, Windows, macOS, and Linux, using a Material You design with Surfboard-like UI.
## Version Notes
- `.fvmrc` pins Flutter 3.35.7 for local development.
- CI uses Flutter 3.41.9. These may diverge; trust CI as the source of truth for release builds.
- Dart SDK constraint: `>=3.8.0 <4.0.0`.
## Build Dependencies
Linux:
```bash
sudo apt-get install libayatana-appindicator3-dev libkeybinder-3.0-dev
```
Windows:
- GCC and Inno Setup.
- `ANDROID_NDK` env var for Android builds.
macOS:
```bash
npm install -g appdmg
```
+47
View File
@@ -0,0 +1,47 @@
# Rules
These are repository coding and testing conventions. Codex command permission rules belong in `.codex/rules/*.rules`; see `.agents/agent-config.md` before adding those.
## Dart and Flutter Style
`analysis_options.yaml` enforces these non-default rules:
- `prefer_single_quotes: true`: always use single quotes.
- `require_trailing_commas: true`: use trailing commas in multi-line argument lists.
- `sort_child_properties_last: true`: `child:` must be the last named parameter.
- `avoid_print: true`: do not use `print()` calls.
- `prefer_const_constructors: true` and `prefer_const_declarations: true`.
- `prefer_final_locals: true` and `prefer_final_in_for_each: true`.
- `always_declare_return_types: true`.
Generated directories are excluded from analysis:
- `build/**`
- `lib/l10n/intl/**`
- `lib/**/generated/**`
- `plugins/**`
## Testing Rules
Use `CoreController.test(mock)` to inject a mocked `CoreHandlerInterface`. Call `CoreController.resetInstance()` in `tearDown` to clean up the singleton between tests.
Register fallback values for freezed params used with `any()` matchers.
Use `ProviderContainer` directly for simple Riverpod provider tests. The generated Riverpod `update()` method takes a callback:
```dart
notifier.update((state) => newValue);
```
When testing freezed models with nested objects, always round-trip through `jsonEncode` and `jsonDecode`. Direct `fromJson(toJson())` fails for nested freezed types because `toJson()` stores child objects directly instead of maps.
## Generated Code
Do not manually edit generated files under:
- `lib/models/generated/`
- `lib/providers/generated/`
- `lib/database/generated/`
- `lib/l10n/intl/`
After schema, model, or provider changes, run build generation and include focused tests when behavior changes.
+17
View File
@@ -0,0 +1,17 @@
# Agent Skills
Repo-scoped Codex skills live under `.agents/skills/*/SKILL.md`. Codex can discover these by skill `name` and `description`, then load the full instructions only when a task matches.
## Available Repo Skills
- `localization`: hardcoded UI text scans, ARB updates, locale generation, and localization verification.
- `provider-tests`: Riverpod provider, notifier, and state-management tests.
- `ui-work`: Flutter UI, widgets, Material You styling, navigation surfaces, and user-facing interactions.
- `core-platform`: core integration, platform managers, Go core communication, desktop/mobile behavior, and Windows helper flow.
## Authoring Notes
- Add new repeatable workflows as `.agents/skills/<skill-name>/SKILL.md`.
- Keep skill descriptions trigger-focused and start them with `Use when...`.
- Keep long reference material in `.agents/*.md`; skills should link to it instead of duplicating it.
- Put command permission rules in `.codex/rules/*.rules`, not in this file.
+33
View File
@@ -0,0 +1,33 @@
---
name: core-platform
description: Use when changing FlClash core integration, platform managers, Go core communication, desktop/mobile platform behavior, or Windows helper flow.
---
# Core And Platform
## When To Use
Use this for changes touching `lib/core/`, `lib/manager/`, `core/`, `services/helper/`, build hooks, system proxy, tray, VPN, TUN, or platform-specific desktop/mobile behavior.
## Workflow
1. Identify which boundary owns the behavior:
- Android lib mode: `lib/core/lib.dart`.
- Desktop process/socket mode: `lib/core/service.dart`.
- Shared facade: `lib/core/controller.dart` and `lib/core/interface.dart`.
- Platform lifecycle: `lib/manager/`.
2. Route feature code through `CoreController` and `CoreHandlerInterface`; avoid direct calls to platform implementations outside their boundary.
3. Keep desktop and mobile paths explicit.
4. For action-layer behavior, inspect `lib/providers/action.dart` and relevant generated providers.
5. Add or update shared Dart tests for logic that can be isolated.
6. Manually verify native behavior when automated coverage is not practical.
## Reference Files
Read `.agents/architecture.md` for the current core modes, manager stack, build hooks, local plugins, and Windows helper notes.
## Pitfalls
- Debug Windows helper auth differs from release token verification.
- `plugins/setup/` is a build harness, not a Dart API plugin.
- Build hooks can trigger Go or Rust compilation indirectly through Flutter platform builds.
+45
View File
@@ -0,0 +1,45 @@
---
name: localization
description: Use when changing FlClash UI text, scanning for hardcoded Chinese strings, updating ARB localization, or fixing generated locale output in this repository.
---
# Localization
## When To Use
Use this for localization work inside `/Users/Shared/follow/FlClash`, especially hardcoded UI text in `lib/`, ARB updates, missing translations, or generated `lib/l10n/` output that does not match source ARB values.
Do not use this for README translation sync or manual edits to generated localization Dart.
## Workflow
1. Confirm `pubspec.yaml` still uses `flutter_intl`, source ARBs under `arb/`, and generated output under `lib/l10n/`.
2. Scan user-facing Dart text before opening many files:
```bash
rg -n "[\\p{Han}]" lib -g '!lib/l10n/intl/**' -g '!lib/**/generated/**'
```
3. Inspect the smallest relevant call sites and nearby ARB keys.
4. Add or update every source ARB:
- `arb/intl_en.arb`
- `arb/intl_zh_CN.arb`
- `arb/intl_ja.arb`
- `arb/intl_ru.arb`
5. Replace inline strings with existing project accessors:
- Widgets with `BuildContext`: `context.appLocalizations.key` from `common.dart`.
- Controllers/providers/non-widget code: `currentAppLocalizations.key` from `app_localizations.dart`.
6. Regenerate:
```bash
dart run intl_utils:generate
```
7. Verify changed Dart files with `flutter analyze` when practical.
8. Re-run the Han-text scan for targeted strings.
## Pitfalls
- If ja/ru still show English, fix ja/ru source ARB values and regenerate. Do not edit generated Dart.
- Ignore `lib/l10n/intl/**` and `lib/**/generated/**` during text scans.
- If generator or analyzer hits local cache permission friction, rerun serially before treating it as a code issue.
+37
View File
@@ -0,0 +1,37 @@
---
name: provider-tests
description: Use when adding or updating FlClash Riverpod provider tests, notifier tests, or state-management tests in this repository.
---
# Provider Tests
## When To Use
Use this for tests under `test/providers/` or any change that validates Riverpod providers, generated notifiers, app state defaults, or provider interactions.
For broader test expansion, pair this with `.agents/rules.md` and `.agents/commands.md`.
## Workflow
1. Read the provider under test and its generated public API before writing assertions.
2. Use `ProviderContainer` directly when no widget tree is needed.
3. Dispose containers in teardown or with `addTearDown(container.dispose)`.
4. Prefer generated notifier APIs over implementation details. Generated `update()` takes a callback:
```dart
notifier.update((state) => newValue);
```
5. Mock external dependencies with `mocktail`; register fallback values for freezed params used with `any()`.
6. Keep tests focused on behavior: defaults, state transitions, persistence boundaries, and side effects.
7. Run the narrowest relevant test first:
```bash
flutter test test/providers/
```
## Pitfalls
- Do not use `dart test`; FlClash models and provider tests may depend on Flutter types.
- Re-check source defaults before asserting them; provider defaults can drift.
- If async provider timing matters, wait on provider futures or state changes instead of fixed sleeps.
+32
View File
@@ -0,0 +1,32 @@
---
name: ui-work
description: Use when changing FlClash Flutter UI, widgets, screens, Material You styling, navigation surfaces, or user-facing interactions.
---
# UI Work
## When To Use
Use this for user-facing Flutter UI changes in `lib/`, including widgets, screens, navigation surfaces, settings rows, dialogs, and interaction behavior.
## Workflow
1. Locate existing nearby widgets and reuse their patterns before adding new abstractions.
2. Follow current Material You and Surfboard-like visual conventions.
3. Use existing providers, notifiers, and helpers where possible.
4. Keep `child:` last in widget constructors.
5. Prefer `const` constructors and final locals.
6. Localize user-facing text through ARB; use `localization` when text changes are non-trivial.
7. Add focused widget tests when behavior changes, especially for rendering states, taps, scrolling, and empty/error states.
8. Run targeted verification:
```bash
flutter analyze
flutter test test/widgets/
```
## Pitfalls
- Do not introduce a new visual system for one screen.
- Do not manually edit generated localization or provider files.
- Avoid broad layout rewrites unless the requested change requires them.
-93
View File
@@ -1,93 +0,0 @@
Scan `lib/` for hardcoded Chinese strings and internationalize them following the project's ARB conventions.
## Instructions
You are performing i18n extraction for a Flutter project that uses the Flutter Intl plugin with ARB files.
### Project localization context
- **ARB files**: `arb/intl_en.arb`, `arb/intl_zh_CN.arb`, `arb/intl_ja.arb`, `arb/intl_ru.arb`
- **Key conventions**: camelCase, flat keys (no nesting, no `@` metadata), semantic naming
- Short labels: `rule`, `global`, `direct`, `edit`, `confirm`
- Descriptive suffix `Desc`: `themeDesc`, `tunDesc`, `logsDesc`
- Validation suffix: `profileNameNullValidationDesc`
- **Generated code**: `lib/l10n/l10n.dart` (DO NOT edit generated files)
- **Access patterns**:
- In widgets with BuildContext: `context.appLocalizations.key` (import `common.dart`)
- In controllers/providers/non-widget code: `currentAppLocalizations.key` (import `app_localizations.dart`)
### Step 1: Find hardcoded Chinese strings
Use Grep to search `lib/` for Chinese characters. The regex pattern for Chinese characters is `[\x{4e00}-\x{9fff}]`.
Important filters:
- **Exclude** `lib/l10n/` (generated localization files)
- **Exclude** lines that already use `appLocalizations` or `currentAppLocalizations`
- **Exclude** false positives that are NOT Chinese text:
- Arrow symbols: `↑` `↓` `←` `→` (Unicode arrows, not CJK)
- Middle dot: `·` (U+00B7, not CJK)
- Command symbol: `⌘` (U+2318, not CJK)
- Other CJK punctuation that is used as UI symbols, not translatable text
For each match, read the surrounding context (5-10 lines) to understand:
- What the string represents (label, error message, tooltip, dialog text, etc.)
- Whether it's inside a widget (has `BuildContext` available) or non-widget code
- Whether it appears in a `Text()`, `TextSpan()`, `title:`, `label:`, or other text position
### Step 2: Generate ARB keys
For each hardcoded Chinese string:
1. **Choose a key name** following project conventions:
- Use the Chinese meaning to derive an English camelCase key
- Keep it concise: `createConfig` not `createAConfigurationForTheProfile`
- Use `Desc` suffix for descriptive/explanatory text
- Use existing similar keys as reference (check `arb/intl_en.arb` for patterns)
2. **Determine the English translation** (the value for `intl_en.arb`)
3. **Determine the Chinese translation** (the value for `intl_zh_CN.arb`) — this is the original hardcoded string
4. **For ja and ru**: use the English translation as a placeholder (translators will fill these in later)
### Step 3: Update ARB files
For each new key, add it to all 4 ARB files. Read each ARB file first to find the right insertion point (alphabetical order is not required, but appending near the end is clean).
Format: add a new line like `"keyName": "value",` before the closing `}`.
### Step 4: Replace hardcoded strings in Dart files
Replace each hardcoded Chinese string with the appropriate localization call:
- If inside a widget BuildContext: `context.appLocalizations.keyName`
- If in controller/provider code: `currentAppLocalizations.keyName`
Ensure the correct import exists:
- For `context.appLocalizations`: the file should import `common.dart` (check if it already does)
- For `currentAppLocalizations`: the file should import `app_localizations.dart` (check if it already does)
If the file uses string interpolation (e.g., `'创建 $name 配置'`), convert to the ARB parameterized format:
- In ARB: `"keyName": "Create $name config"`
- In Dart: `currentAppLocalizations.keyName(name)`
### Step 5: Report changes
After all modifications, output a summary:
1. List each file modified and what changed
2. List each new ARB key added with its translations
3. Remind the user to run code generation if needed:
```
dart run build_runner build --delete-conflicting-outputs
```
or trigger Flutter Intl regeneration in their IDE.
### Important notes
- Only extract strings that are user-facing UI text. Do NOT extract:
- Comments (lines starting with `//`)
- Log messages (unless they appear in the UI)
- Variable names or identifiers
- String literals used as keys/identifiers (not displayed to users)
- If a Chinese string is split across multiple lines or concatenations, merge them into a single ARB entry
- If a string contains dynamic content (variables, URLs), use ARB parameter syntax `$variableName`
+5
View File
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
+12
View File
@@ -0,0 +1,12 @@
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "codegraph",
"args": [
"serve",
"--mcp"
]
}
}
}
+39
View File
@@ -0,0 +1,39 @@
cask "flclash" do
version "VERSION"
on_macos do
arch arm: "arm64", intel: "amd64"
sha256 arm: "ARM_SHA256",
intel: "AMD_SHA256"
url "https://github.com/chen08209/FlClash/releases/download/v#{version}/FlClash-#{version}-macos-#{arch}.dmg"
end
name "FlClash"
desc "Multi-platform proxy client based on ClashMeta"
homepage "https://github.com/chen08209/FlClash"
livecheck do
url :url
strategy :github_latest
end
depends_on :macos
app "FlClash.app"
postflight do
system_command "xattr",
args: ["-rd", "com.apple.quarantine", "#{appdir}/FlClash.app"]
end
uninstall quit: "com.follow.clash"
zap trash: [
"~/Library/Application Support/com.follow.clash",
"~/Library/Caches/com.follow.clash",
"~/Library/Preferences/com.follow.clash.plist",
"~/Library/Saved Application State/com.follow.clash.savedState",
]
end
+1 -1
View File
@@ -42,7 +42,7 @@
<td>
<a href="https://github.com/chen08209/FlClash/releases/download/vVERSION/FlClash-VERSION-linux-amd64.AppImage"><img src="https://img.shields.io/badge/AppImage-x64-f84e29.svg?logo=linux"> </a><br>
<a href="https://github.com/chen08209/FlClash/releases/download/vVERSION/FlClash-VERSION-linux-amd64.deb"><img src="https://img.shields.io/badge/DebPackage-x64-FF9966.svg?logo=debian"> </a><br>
<a href="https://github.com/chen08209/FlClash/releases/download/vVERSION/FlClash-VERSION-linux-amd64.deb"><img src="https://img.shields.io/badge/RpmPackage-x64-F1B42F.svg?logo=redhat"> </a>
<a href="https://github.com/chen08209/FlClash/releases/download/vVERSION/FlClash-VERSION-linux-amd64.rpm"><img src="https://img.shields.io/badge/RpmPackage-x64-F1B42F.svg?logo=redhat"> </a>
</td>
</tr>
</tbody>
+54 -23
View File
@@ -6,6 +6,7 @@ on:
- 'v*'
env:
IS_STABLE: ${{ !contains(github.ref, '-') }}
FLUTTER_VERSION: '3.44.4'
jobs:
test:
@@ -21,7 +22,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.41.9
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install dependencies
@@ -63,6 +64,7 @@ jobs:
steps:
- name: Setup rust
if: startsWith(matrix.os, 'windows-11-arm')
shell: pwsh
run: |
Invoke-WebRequest -Uri "https://win.rustup.rs/aarch64" -OutFile rustup-init.exe
.\rustup-init.exe -y --default-toolchain stable
@@ -79,14 +81,16 @@ jobs:
run: |
echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/app/keystore.jks
echo "${{ secrets.SERVICE_JSON }}" | base64 --decode > android/app/google-services.json
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/local.properties
echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> android/local.properties
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/local.properties
{
echo "keyAlias=${{ secrets.KEY_ALIAS }}"
echo "storePassword=${{ secrets.STORE_PASSWORD }}"
echo "keyPassword=${{ secrets.KEY_PASSWORD }}"
} >> android/local.properties
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24.0'
go-version: '1.26.4'
cache-dependency-path: |
core/go.sum
@@ -95,20 +99,18 @@ jobs:
run: git config --global core.longpaths true
- name: Setup Flutter
if: ${{ !(startsWith(matrix.os, 'windows-11-arm') || startsWith(matrix.os, 'ubuntu-24.04-arm')) }}
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.41.9
cache: true
- name: Setup Flutter With Other
if: startsWith(matrix.os, 'windows-11-arm') || startsWith(matrix.os, 'ubuntu-24.04-arm')
uses: subosito/flutter-action@v2
with:
channel: master
flutter-version: 3.44.0-0.1.pre
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Setup Xcode
if: startsWith(matrix.platform,'macos')
run: |
sudo xcode-select -s /Applications/Xcode_26.3.app || sudo xcode-select -s /Applications/Xcode_26.2.app
xcodebuild -version
- name: Setup NDK
if: startsWith(matrix.platform,'android')
uses: nttld/setup-ndk@v1
@@ -133,17 +135,18 @@ jobs:
overwrite: true
changelog:
if: ${{ !contains(github.ref, '-') }}
permissions:
contents: write
runs-on: ubuntu-latest
needs: [ build ]
steps:
- name: Checkout
uses: actions/checkout@v4
if: ${{ env.IS_STABLE == 'true' }}
with:
fetch-depth: 0
ref: refs/heads/main
- name: Generate
if: ${{ env.IS_STABLE == 'true' }}
run: |
last_ver=$(grep -m1 '^## ' CHANGELOG.md 2>/dev/null | sed 's/^## //')
@@ -167,7 +170,6 @@ jobs:
mv "$temp" CHANGELOG.md
- name: Commit
if: ${{ env.IS_STABLE == 'true' }}
run: |
git add CHANGELOG.md
if ! git diff --cached --quiet; then
@@ -175,8 +177,7 @@ jobs:
git config --local user.email "chen08209@gmail.com"
git config --local user.name "chen08209"
git commit -m "Update changelog"
git push
if [ $? -eq 0 ]; then
if git push; then
echo "Push succeeded"
else
echo "Push failed"
@@ -188,7 +189,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
upload:
permissions: write-all
permissions:
contents: write
needs: [ build ]
runs-on: ubuntu-latest
services:
@@ -251,7 +253,7 @@ jobs:
if: ${{ env.IS_STABLE == 'true' }}
run: |
cd ./dist
sha256sum * > SHA256SUMS
sha256sum ./* > SHA256SUMS
- name: Release
if: ${{ env.IS_STABLE == 'true' }}
@@ -260,6 +262,35 @@ jobs:
files: ./dist/*
body_path: './release.md'
- name: Create Homebrew Cask Source Dir
if: ${{ env.IS_STABLE == 'true' }}
run: |
version=$(echo "${{ github.ref_name }}" | sed 's/^v//')
arm_sha=$(sha256sum "./dist/FlClash-${version}-macos-arm64.dmg" | awk '{print $1}')
amd_sha=$(sha256sum "./dist/FlClash-${version}-macos-amd64.dmg" | awk '{print $1}')
mkdir -p ./brew/Casks
sed \
-e "s|VERSION|$version|g" \
-e "s|ARM_SHA256|$arm_sha|g" \
-e "s|AMD_SHA256|$amd_sha|g" \
./.github/homebrew_cask_template.rb > ./brew/Casks/flclash.rb
- name: Push to Homebrew tap
if: ${{ env.IS_STABLE == 'true' }}
uses: cpina/github-action-push-to-another-repository@v1.7.2
env:
SSH_DEPLOY_KEY: ${{ secrets.HOMEBREW_SSH_DEPLOY_KEY }}
with:
source-directory: ./brew/
destination-github-username: chen08209
destination-repository-name: homebrew-tap
user-name: 'chen08029'
user-email: 'chen08209@gmail.com'
target-branch: main
commit-message: Update Homebrew cask from ${{ github.ref_name }}
target-directory: /
- name: Create Fdroid Source Dir
if: ${{ env.IS_STABLE == 'true' }}
run: |
@@ -276,8 +307,8 @@ jobs:
source-directory: ./tmp/
destination-github-username: chen08209
destination-repository-name: FlClash-fdroid-repo
user-name: 'github-actions[bot]'
user-email: 'github-actions[bot]@users.noreply.github.com'
user-name: 'chen08029'
user-email: 'chen08209@gmail.com'
target-branch: main
commit-message: Update from ${{ github.ref_name }}
target-directory: /tmp/
-10
View File
@@ -2,13 +2,3 @@
path = core/Clash.Meta
url = git@github.com:chen08209/Clash.Meta.git
branch = FlClash
[submodule "plugins/flutter_distributor"]
path = plugins/flutter_distributor
url = git@github.com:chen08209/flutter_distributor.git
branch = FlClash
[submodule "plugins/tray_manager"]
path = plugins/tray_manager
url = git@github.com:chen08209/tray_manager.git
branch = main
+12
View File
@@ -0,0 +1,12 @@
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "codegraph",
"args": [
"serve",
"--mcp"
]
}
}
}
+20
View File
@@ -0,0 +1,20 @@
repos:
- repo: local
hooks:
- id: dart-format
name: dart format
entry: dart format
language: system
files: \.dart$
- id: flutter-analyze
name: flutter analyze
entry: flutter analyze --no-fatal-infos
language: system
pass_filenames: false
files: \.dart$
- id: flutter-test
name: flutter test
entry: flutter test --reporter expanded
language: system
pass_filenames: false
files: \.dart$
+13
View File
@@ -0,0 +1,13 @@
{
"lsp": {
"gopls": {
"binary": {
"env": {
"CGO_ENABLED": "0",
// "GOOS": "android",
// "GOARCH": "arm64",
},
},
},
},
}
+24 -222
View File
@@ -1,233 +1,35 @@
# AGENTS.md
This file provides guidance for AI coding agents working with code in this repository.
This file is the entry point for AI coding agents working in this repository. Keep it small: detailed guidance lives under
`.agents/`, and discoverable repo skills live under `.agents/skills/*/SKILL.md`.
## Project Overview
## Start Here
FlClash is a multi-platform proxy client based on ClashMeta (mihomo), built with Flutter. Supports Android, Windows,
macOS, and Linux. Material You design with Surfboard-like UI.
Read these files before making changes:
## Common Development Commands
- [.agents/project.md](.agents/project.md): project overview, versions, and build dependencies.
- [.agents/commands.md](.agents/commands.md): build, development, code generation, and test commands.
- [.agents/rules.md](.agents/rules.md): lint, testing, generated-code, and workflow rules.
### Building
Read these only when the task touches their area:
```bash
# Update submodules first (ClashMeta Go core lives in core/Clash.Meta/)
git submodule update --init --recursive
- [.agents/architecture.md](.agents/architecture.md): core integration, providers, database, managers, build system, and
local plugins.
- [.agents/agent-config.md](.agents/agent-config.md): how to choose between `AGENTS.md`, `.agents`, skills, Codex config,
command rules, and hooks.
- [.agents/skills.md](.agents/skills.md): index of repo-scoped skills in `.agents/skills/`.
# Full package build (Go core + Flutter + packaging) via setup.dart
dart setup.dart macos
dart setup.dart linux
dart setup.dart windows
dart setup.dart android
## Highest Priority Rules
# Build only the Go core (skip Flutter packaging)
bash plugins/setup/buildkit/run_build_tool.sh macos
bash plugins/setup/buildkit/run_build_tool.sh linux
bash plugins/setup/buildkit/run_build_tool.sh windows
bash plugins/setup/buildkit/run_build_tool.sh android
```
- Use `flutter test`, not `dart test`, because models pull in Flutter types.
- Run code generation after modifying models, providers, or database schema.
- Do not manually edit generated files.
- Follow `analysis_options.yaml`, especially single quotes, trailing commas, `child:` last, no `print()`, const/final
preferences, and declared return types.
- For CI parity, verify with `flutter pub get`, `flutter analyze --no-fatal-infos`, and
`flutter test --reporter expanded` when practical.
### Flutter Development
## Repo Skills
```bash
# Project is pinned with FVM (.fvmrc currently uses Flutter 3.35.7)
fvm flutter pub get
fvm flutter run
fvm flutter test
# Plain Flutter also works when your global SDK matches the project constraints
flutter pub get
flutter run # Run on connected device/desktop
flutter test # Run all tests (use flutter test, not dart test — models pull in Flutter types)
```
### Code Generation
Required after modifying models, providers, or database schema:
```bash
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch # Continuous regeneration
```
Code generation covers: Riverpod providers (`riverpod_generator`), models (`freezed`, `json_serializable`), and database
tables (`drift_dev`).
### Testing
Tests use `package:test/test.dart` for pure Dart logic (common utils, models) and `flutter_test` for provider/widget tests.
`mocktail` is the mocking framework.
```bash
flutter test test/models/ # Model serialization & extension round-trip tests
flutter test test/core/ # CoreController tests (mocked CoreHandlerInterface)
flutter test test/providers/ # Riverpod provider tests (config & app state notifiers)
flutter test test/common/ # Utility function tests (utils, string, iterable, fixed, etc.)
flutter test test/database/ # Database type converter tests
flutter test test/widgets/ # Widget-level rendering/interaction tests
flutter test test/setup_test.dart
flutter test plugins/proxy/test/proxy_test.dart # Dart tests for bundled plugin packages
```
Root `flutter test` only discovers the root package's `test/` directory by default. Include bundled plugin Dart tests by
passing their paths explicitly, or run `flutter test` from that plugin package directory. Native plugin tests under
platform folders (for example Windows C++ tests) are not run by `flutter test`.
**Mocking `CoreHandlerInterface`:** Use `CoreController.test(mock)` to inject a mock interface. Call
`CoreController.resetInstance()` in `tearDown` to clean up the singleton between tests. Remember to
`registerFallbackValue()` for freezed params used with `any()` matchers.
**Provider tests:** Use `ProviderContainer` directly (no widget tree needed for simple notifiers). The Riverpod
generated `update()` method takes a callback: `notifier.update((state) => newValue)`.
**Model round-trip tests:** Always go through `jsonEncode`/`jsonDecode` when testing freezed models with
nested objects — `toJson()` stores child objects directly (not as maps), so direct `fromJson(toJson())`
fails for nested freezed types.
### Build Dependencies
**Linux:** `sudo apt-get install libayatana-appindicator3-dev libkeybinder-3.0-dev`
**Windows:** GCC and Inno Setup. `ANDROID_NDK` env var for Android builds.
**macOS:** `npm install -g appdmg` for DMG creation.
## Architecture
### Core Integration (Go ClashMeta <-> Flutter)
This is the most important architectural concept. The Go proxy core (`core/`) operates in two modes:
- **Android (lib mode):** Go core compiled as C shared library (`libclash.so`) via `go build -buildmode=c-shared` with
CGO. Flutter calls it via FFI through the `service` plugin. Dart-side: `lib/core/lib.dart` (`CoreLib` class).
- **Desktop (core mode):** Go core runs as a separate process with `CGO_ENABLED=0`. Flutter communicates via
JSON-over-socket (Unix socket on macOS/Linux, TCP on Windows). Dart-side: `lib/core/service.dart` (`CoreService`
class).
`lib/core/controller.dart` (`CoreController`) selects the implementation based on platform. `lib/core/interface.dart`
defines the shared `CoreHandlerInterface`.
Go core key files: `core/hub.go` (handler functions), `core/action.go` (dispatch), `core/lib.go` (CGO exports),
`core/server.go` (socket server).
### State Management (Riverpod)
Provider files in `lib/providers/`:
- `app.dart` - Runtime/UI state (logs, traffic, delays, loading, navigation)
- `config.dart` - Persistent config providers (app settings, theme, VPN, proxy style)
- `state.dart` - Derived/computed providers (navigation, proxy, tray, color scheme)
- `action.dart` - Business logic notifiers (setup, backup, core lifecycle, proxy selection)
- `database.dart` - Drift database provider wrappers
`globalState` (`lib/state.dart`) is a singleton holding app lifecycle, timers, theme, and the start/stop state.
Providers are generated into `lib/providers/generated/`.
### Database (Drift/SQLite)
Type-safe SQLite via Drift in `lib/database/`. Current schema version is 2. Tables are `Profiles`, `Scripts`, `Rules`,
`ProfileRuleLinks` (`profile_rule_mapping`), `ProxyGroups`, and `IconRecords` (`icon_records`). Rule scenes distinguish
global added rules, profile added rules, profile custom rules, and disabled links. Uses fractional indexing for rule and
proxy-group ordering.
Generated Drift output lives in `lib/database/generated/database.g.dart`. After schema changes, run code generation and
add/update focused database tests under `test/database/` when converter or migration behavior changes.
### Manager Stack (Widget Tree)
Managers are nested InheritedWidgets/StatefulWidgets in `lib/application.dart`:
```
AppEnvManager > StatusManager > ThemeManager
> [Desktop: WindowManager > TrayManager > HotKeyManager > ProxyManager]
> ConnectivityManager > CoreManager > AppStateManager
> [Mobile: AndroidManager > VpnManager | Desktop: WindowHeaderContainer]
```
Each manager in `lib/manager/` handles a specific platform concern. Desktop-only managers are conditionally inserted.
### Core Controller + Actions
`lib/core/controller.dart` (`CoreController`) is a singleton facade over `CoreHandlerInterface`. All 25+ public methods
delegate to the platform-specific interface (Android FFI or desktop socket). Has `@visibleForTesting` constructor and
`resetInstance()` for test injection.
Business logic lives in Riverpod notifier classes in `lib/providers/action.dart` (~960 lines, should be split):
- `CommonAction` — update check, common UI operations
- `SetupAction` — config setup, TUN management
- `BackupAction` — backup/restore with WebDAV sync
- `CoreAction` — core lifecycle (init, connect, restart, shutdown)
- `SystemAction` — system integration (tray, exit, brightness)
- `StoreAction` — profile storage operations
- `ThemeAction` — theme state updates
- `ProxiesAction` — group management, proxy selection
- `ProfilesAction` — profile CRUD, auto-update, import
### Platform Managers (`lib/manager/`)
Desktop: `WindowManager`, `TrayManager`, `HotKeyManager`, `ProxyManager`
Mobile: `AndroidManager`, `TileManager`, `VpnManager`
Shared: `ConnectivityManager`, `CoreManager`, `AppStateManager`, `StatusManager`, `ThemeManager`
### Build System
`setup.dart` (project root) is the release build orchestrator:
1. On Windows, pre-builds Go core via `dart run build_tool windows` and reads `core_sha256.json`
2. Writes `env.json` (APP_ENV)
3. Passes SHA256 as `--dart-define=CORE_SHA256=$val` (compile-time embedded, secure; Windows only)
4. Activates `flutter_distributor` for packaging
Go core building is handled by `build_tool`, a standalone Dart CLI in `plugins/setup/buildkit/build_tool/`.
Platform build hooks inside `flutter build` trigger `build_tool` automatically:
- **macOS:** podspec script phase → `build_pod.sh` → `build_tool macos`
- **Linux:** CMake include → `buildkit/cmake/buildkit.cmake` → `build_tool linux`
- **Windows:** CMake include → `buildkit/cmake/buildkit.cmake` → `build_tool windows` (debug: `--dev` via `CMAKE_BUILD_TYPE`)
- **Android:** Gradle include → `buildkit/gradle/plugin.gradle` → `build_tool android`
**Windows helper auth (release):** Core SHA256 is embedded in both the Flutter app (`--dart-define`) and the Rust
helper (`TOKEN` env var during cargo build). The Dart app pings the helper and verifies the token matches.
**Windows helper auth (debug):** The Rust helper skips token verification when built in debug mode
(`cfg!(debug_assertions)`), so `flutter run` works without any SHA256 dance.
`plugins/setup/` is an FFI plugin that exists solely as a build harness — it carries no Dart API, only platform build hooks
(podspec, CMake, Gradle) that trigger Go compilation. Windows builds also compile a Rust helper (`services/helper/`) via
`RustBuilder`.
Build configuration defaults live in `build_tool/lib/src/options.dart` and can be overridden via `build_config.yaml`
in the project root.
Architecture detection is automatic (host arch via `uname -m` on Unix, `PROCESSOR_ARCHITECTURE` on Windows). The
`--description` flag passed to flutter_distributor adds arch suffix to artifact names (e.g.,
`FlClash-0.8.93-macos-arm64.dmg`).
### Local Plugins (`plugins/`)
- `setup` - Build harness FFI plugin (triggers Go/Rust compilation per platform)
- `proxy` - System proxy configuration
- `rust_api` - Flutter Rust Bridge FFI plugin (named pipe / local socket communication)
- `tray_manager` - System tray (forked/custom)
- `wifi_ssid` - Wi-Fi SSID detection
- `window_ext` - Window extensions
- `flutter_distributor` - App packaging/distribution
### Rust Helper Service (`services/helper/`)
Windows-only privileged helper for starting the core as admin and managing TUN. Built with
`cargo build --release --features windows-service`. Token-based auth with Flutter app.
### Localization
ARB files in `arb/`. Generated via `flutter_intl` into `lib/l10n/`. Use `AppLocalizations.of(context)!` for strings.
**Supported locales:** `en`, `zh_CN`, `ja`, `ru`
**Access patterns:**
- In widgets with BuildContext: `context.appLocalizations.key` (import `common.dart`)
- In controllers/providers/non-widget code: `currentAppLocalizations.key` (import `app_localizations.dart`)
Use repo skills from `.agents/skills/` when a task matches their descriptions. Current skills cover localization,
provider tests, UI work, and core/platform changes.
+3 -4
View File
@@ -1,5 +1,4 @@
# CLAUDE.md - Claude Code instructions
# CLAUDE.md
Please read AGENTS.md for all project guidelines and conventions.
@AGENTS.md
This repository uses [AGENTS.md](AGENTS.md) as the canonical agent entry point.
Read that file first, then follow the `.agents/` references it routes to.
+33
View File
@@ -0,0 +1,33 @@
SHELL := /bin/bash
PLATFORM ?= macos
BUILDKIT := plugins/setup/buildkit/run_build_tool.sh
ARCH_ARG := $(if $(ARCH),--arch $(ARCH),)
TARGET_PLATFORM_ARG := $(if $(TARGET_PLATFORM),--target-platform $(TARGET_PLATFORM),)
.PHONY: help submodules core core-macos core-linux core-windows core-android
help:
@echo 'make core # build macOS core by default'
@echo 'make core PLATFORM=linux ARCH=amd64'
@echo 'make core-macos ARCH=arm64'
@echo 'make core-android ARCH=arm64'
@echo 'make core-android TARGET_PLATFORM=android-arm64'
submodules:
git submodule update --init --recursive
core:
bash $(BUILDKIT) $(PLATFORM) $(ARCH_ARG) $(TARGET_PLATFORM_ARG)
core-macos:
$(MAKE) core PLATFORM=macos
core-linux:
$(MAKE) core PLATFORM=linux
core-windows:
$(MAKE) core PLATFORM=windows
core-android:
$(MAKE) core PLATFORM=android
+8 -1
View File
@@ -61,6 +61,13 @@ Support the following actions
<a href="https://chen08209.github.io/FlClash-fdroid-repo/repo?fingerprint=789D6D32668712EF7672F9E58DEEB15FBD6DCEEC5AE7A4371EA72F2AAE8A12FD"><img alt="Get it on F-Droid" src="snapshots/get-it-on-fdroid.svg" width="200px"/></a> <a href="https://github.com/chen08209/FlClash/releases"><img alt="Get it on GitHub" src="snapshots/get-it-on-github.svg" width="200px"/></a>
### Homebrew
```bash
brew tap chen08209/tap
brew install --cask flclash
```
## Build
1. Update submodules
@@ -129,4 +136,4 @@ The easiest way to support developers is to click on the star (⭐) at the top o
<a href="https://api.star-history.com/svg?repos=chen08209/FlClash&Date">
<img alt="start" width=50% src="https://api.star-history.com/svg?repos=chen08209/FlClash&Date"/>
</a>
</p>
</p>
+7
View File
@@ -61,6 +61,13 @@ on Mobile:
<a href="https://chen08209.github.io/FlClash-fdroid-repo/repo?fingerprint=789D6D32668712EF7672F9E58DEEB15FBD6DCEEC5AE7A4371EA72F2AAE8A12FD"><img alt="Get it on F-Droid" src="snapshots/get-it-on-fdroid.svg" width="200px"/></a> <a href="https://github.com/chen08209/FlClash/releases"><img alt="Get it on GitHub" src="snapshots/get-it-on-github.svg" width="200px"/></a>
### Homebrew
```bash
brew tap chen08209/tap
brew install --cask flclash
```
## Build
1. 更新 submodules
+2 -3
View File
@@ -3,14 +3,13 @@ import java.util.Properties
plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
val localPropertiesFile = rootProject.file("local.properties")
val localProperties = Properties().apply {
val localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
localPropertiesFile.inputStream().use { load(it) }
}
@@ -106,4 +105,4 @@ dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics.ndk)
implementation(libs.firebase.analytics)
}
}
+15
View File
@@ -15,10 +15,25 @@ subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
gradle.projectsEvaluated {
allprojects {
tasks.withType<JavaCompile>().configureEach {
sourceCompatibility = JavaVersion.VERSION_17.toString()
targetCompatibility = JavaVersion.VERSION_17.toString()
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}
}
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}
+1 -2
View File
@@ -2,7 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
@@ -42,4 +41,4 @@ dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics.ndk)
implementation(libs.firebase.analytics)
}
}
View File
-1
View File
@@ -2,7 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
+4
View File
@@ -1,3 +1,7 @@
org.gradle.jvmargs=-Xmx4G
android.useAndroidX=true
android.enableJetifier=true
# This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=false
# This newDsl flag was added automatically by Flutter migrator
android.newDsl=false
+5 -6
View File
@@ -1,6 +1,6 @@
[versions]
#agp = "8.10.1"
firebaseBom = "34.2.0"
firebaseBom = "34.15.0"
minSdk = "23"
targetSdk = "36"
compileSdk = "36"
@@ -9,13 +9,12 @@ coreKtx = "1.17.0"
annotationJvm = "1.9.1"
coreSplashscreen = "1.0.1"
gson = "2.13.1"
kotlin = "2.2.10"
kotlin = "2.2.20"
smaliDexlib2 = "3.0.9"
firebaseCrashlyticsKtx = "20.0.1"
firebaseCommonKtx = "22.0.0"
firebaseCrashlyticsKtx = "20.0.6"
firebaseCommonKtx = "22.1.0"
[libraries]
build-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
androidx-core = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
annotation-jvm = { module = "androidx.annotation:annotation-jvm", version.ref = "annotationJvm" }
core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
@@ -25,4 +24,4 @@ firebase-crashlytics-ndk = { module = "com.google.firebase:firebase-crashlytics-
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
smali-dexlib2 = { module = "com.android.tools.smali:smali-dexlib2", version.ref = "smaliDexlib2" }
firebase-crashlytics-ktx = { group = "com.google.firebase", name = "firebase-crashlytics-ktx", version.ref = "firebaseCrashlyticsKtx" }
firebase-common-ktx = { group = "com.google.firebase", name = "firebase-common-ktx", version.ref = "firebaseCommonKtx" }
firebase-common-ktx = { group = "com.google.firebase", name = "firebase-common-ktx", version.ref = "firebaseCommonKtx" }
+1 -1
View File
@@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-all.zip
+1 -3
View File
@@ -2,8 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
}
android {
@@ -45,4 +43,4 @@ dependencies {
implementation(project(":common"))
implementation(libs.gson)
implementation(libs.androidx.core)
}
}
@@ -1,11 +1,36 @@
package com.follow.clash.service.models
import android.os.Parcel
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class NotificationParams(
val title: String = "FlClash",
val stopText: String = "STOP",
val onlyStatisticsProxy: Boolean = false,
) : Parcelable
) : Parcelable {
constructor(parcel: Parcel) : this(
title = parcel.readString() ?: "FlClash",
stopText = parcel.readString() ?: "STOP",
onlyStatisticsProxy = parcel.readByte() != 0.toByte(),
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(title)
parcel.writeString(stopText)
parcel.writeByte(if (onlyStatisticsProxy) 1.toByte() else 0.toByte())
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<NotificationParams> {
override fun createFromParcel(parcel: Parcel): NotificationParams {
return NotificationParams(parcel)
}
override fun newArray(size: Int): Array<NotificationParams?> {
return arrayOfNulls(size)
}
}
}
@@ -1,19 +1,45 @@
package com.follow.clash.service.models
import android.os.Parcel
import android.os.Parcelable
import com.follow.clash.common.AccessControlMode
import kotlinx.parcelize.Parcelize
import java.net.InetAddress
@Parcelize
data class AccessControlProps(
val enable: Boolean,
val mode: AccessControlMode,
val acceptList: List<String>,
val rejectList: List<String>,
) : Parcelable
) : Parcelable {
constructor(parcel: Parcel) : this(
enable = parcel.readByte() != 0.toByte(),
mode = AccessControlMode.valueOf(parcel.readString() ?: AccessControlMode.ACCEPT_SELECTED.name),
acceptList = parcel.createStringArrayList() ?: emptyList(),
rejectList = parcel.createStringArrayList() ?: emptyList(),
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeByte(if (enable) 1.toByte() else 0.toByte())
parcel.writeString(mode.name)
parcel.writeStringList(acceptList)
parcel.writeStringList(rejectList)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<AccessControlProps> {
override fun createFromParcel(parcel: Parcel): AccessControlProps {
return AccessControlProps(parcel)
}
override fun newArray(size: Int): Array<AccessControlProps?> {
return arrayOfNulls(size)
}
}
}
@Parcelize
data class VpnOptions(
val enable: Boolean,
val port: Int,
@@ -25,7 +51,59 @@ data class VpnOptions(
val bypassDomain: List<String>,
val stack: String,
val routeAddress: List<String>,
) : Parcelable
) : Parcelable {
constructor(parcel: Parcel) : this(
enable = parcel.readByte() != 0.toByte(),
port = parcel.readInt(),
ipv6 = parcel.readByte() != 0.toByte(),
dnsHijacking = parcel.readByte() != 0.toByte(),
accessControlProps = readAccessControlProps(parcel),
allowBypass = parcel.readByte() != 0.toByte(),
systemProxy = parcel.readByte() != 0.toByte(),
bypassDomain = parcel.createStringArrayList() ?: emptyList(),
stack = parcel.readString() ?: "",
routeAddress = parcel.createStringArrayList() ?: emptyList(),
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeByte(if (enable) 1.toByte() else 0.toByte())
parcel.writeInt(port)
parcel.writeByte(if (ipv6) 1.toByte() else 0.toByte())
parcel.writeByte(if (dnsHijacking) 1.toByte() else 0.toByte())
parcel.writeParcelable(accessControlProps, flags)
parcel.writeByte(if (allowBypass) 1.toByte() else 0.toByte())
parcel.writeByte(if (systemProxy) 1.toByte() else 0.toByte())
parcel.writeStringList(bypassDomain)
parcel.writeString(stack)
parcel.writeStringList(routeAddress)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<VpnOptions> {
@Suppress("DEPRECATION")
private fun readAccessControlProps(parcel: Parcel): AccessControlProps {
return parcel.readParcelable<AccessControlProps>(
AccessControlProps::class.java.classLoader,
) ?: AccessControlProps(
enable = false,
mode = AccessControlMode.ACCEPT_SELECTED,
acceptList = emptyList(),
rejectList = emptyList(),
)
}
override fun createFromParcel(parcel: Parcel): VpnOptions {
return VpnOptions(parcel)
}
override fun newArray(size: Int): Array<VpnOptions?> {
return arrayOfNulls(size)
}
}
}
data class CIDR(val address: InetAddress, val prefixLength: Int)
@@ -80,4 +158,4 @@ fun String.toCIDR(): CIDR {
}
return CIDR(address, prefixLength)
}
}
+4 -4
View File
@@ -19,10 +19,10 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.12.2" apply false
id("org.jetbrains.kotlin.android") version "2.2.10" apply false
id("com.google.gms.google-services") version ("4.3.15") apply false
id("com.google.firebase.crashlytics") version ("2.8.1") apply false
id("com.android.application") version "9.0.1" apply false
id("org.jetbrains.kotlin.android") version "2.4.0" apply false
id("com.google.gms.google-services") version ("4.5.0") apply false
id("com.google.firebase.crashlytics") version ("3.0.7") apply false
}
+14 -1
View File
@@ -133,6 +133,7 @@
"app": "App",
"general": "General",
"systemProxyDesc": "Attach HTTP proxy to VpnService",
"userAgent": "User-Agent",
"unifiedDelay": "Unified delay",
"unifiedDelayDesc": "Remove extra delays such as handshaking",
"tcpConcurrent": "TCP concurrent",
@@ -537,5 +538,17 @@
"editSsid": "Edit SSID",
"authorized": "Authorized",
"tapToAuthorize": "Tap to authorize",
"suspended": "Suspended..."
"suspended": "Suspended...",
"geoOptions": "Geo Options",
"geoAutoUpdate": "Auto Update",
"geoAutoUpdateInterval": "Auto Update Interval",
"geoAutoUpdateIntervalTip": "Auto update interval must be greater than 0",
"hours": "hours",
"hoursCount": "{count} hours",
"geoResources": "Geo Resources",
"geoUpdating": "Updating {name}...",
"geoSkipped": "{name} skipped",
"geoUpdated": "{name} updated",
"secondsCount": "{count} seconds",
"entriesCount": "{count} entries"
}
+14 -1
View File
@@ -133,6 +133,7 @@
"app": "アプリ",
"general": "一般",
"systemProxyDesc": "HTTPプロキシをVpnServiceに接続",
"userAgent": "ユーザーエージェント",
"unifiedDelay": "統一遅延",
"unifiedDelayDesc": "ハンドシェイクなどの余分な遅延を削除",
"tcpConcurrent": "TCP並列処理",
@@ -537,5 +538,17 @@
"editSsid": "SSIDを編集",
"authorized": "許可済み",
"tapToAuthorize": "タップして許可",
"suspended": "一時停止中..."
"suspended": "一時停止中...",
"geoOptions": "Geoオプション",
"geoAutoUpdate": "自動更新",
"geoAutoUpdateInterval": "自動更新間隔",
"geoAutoUpdateIntervalTip": "自動更新間隔は0より大きくなければなりません",
"hours": "時間",
"hoursCount": "{count} 時間",
"geoResources": "Geoリソース",
"geoUpdating": "{name}を更新中...",
"geoSkipped": "{name} スキップ済み",
"geoUpdated": "{name} 更新済み",
"secondsCount": "{count} 秒",
"entriesCount": "{count} エントリ"
}
+14 -1
View File
@@ -133,6 +133,7 @@
"app": "Приложение",
"general": "Общие",
"systemProxyDesc": "Прикрепить HTTP-прокси к VpnService",
"userAgent": "User-Agent",
"unifiedDelay": "Унифицированная задержка",
"unifiedDelayDesc": "Убрать дополнительные задержки, такие как рукопожатие",
"tcpConcurrent": "TCP параллелизм",
@@ -537,5 +538,17 @@
"editSsid": "Изменить SSID",
"authorized": "Разрешено",
"tapToAuthorize": "Нажмите, чтобы разрешить",
"suspended": "Приостановлено..."
"suspended": "Приостановлено...",
"geoOptions": "Настройки Geo",
"geoAutoUpdate": "Автообновление",
"geoAutoUpdateInterval": "Интервал автообновления",
"geoAutoUpdateIntervalTip": "Интервал автообновления должен быть больше 0",
"hours": "часов",
"hoursCount": "{count} часов",
"geoResources": "Ресурсы Geo",
"geoUpdating": "Обновление {name}...",
"geoSkipped": "{name} пропущено",
"geoUpdated": "{name} обновлено",
"secondsCount": "{count} секунд",
"entriesCount": "{count} записей"
}
+14 -1
View File
@@ -133,6 +133,7 @@
"app": "应用",
"general": "常规",
"systemProxyDesc": "设置系统代理",
"userAgent": "用户代理",
"unifiedDelay": "统一延迟",
"unifiedDelayDesc": "去除握手等额外延迟",
"tcpConcurrent": "TCP并发",
@@ -537,5 +538,17 @@
"editSsid": "编辑SSID",
"authorized": "已授权",
"tapToAuthorize": "点击授权",
"suspended": "挂起中..."
"suspended": "挂起中...",
"geoOptions": "Geo 选项",
"geoAutoUpdate": "自动更新",
"geoAutoUpdateInterval": "自动更新间隔",
"geoAutoUpdateIntervalTip": "自动更新间隔必须大于0",
"hours": "小时",
"hoursCount": "{count} 小时",
"geoResources": "Geo 资源",
"geoUpdating": "正在更新 {name}...",
"geoSkipped": "{name} 已跳过",
"geoUpdated": "{name} 已更新",
"secondsCount": "{count} 秒",
"entriesCount": "{count} 个条目"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 12 KiB

+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="108" height="108" viewBox="54 54 132 132" xmlns="http://www.w3.org/2000/svg">
<title>status_1</title>
<defs>
<filter id="glow-shadow" x="-50%" y="-50%" width="200%" height="200%">
<feDropShadow dx="0" dy="5" stdDeviation="5" flood-color="#0F172A" flood-opacity="0.12"/>
</filter>
<path id="shape-top"
d="M60.65,89.6L154.18,35.6A18,18 107.59,0 1,178.77 42.19L178.77,42.19A18,18 107.59,0 1,172.18 66.78L78.65,120.78A18,18 106.67,0 1,54.06 114.19L54.06,114.19A18,18 106.67,0 1,60.65 89.6z"/>
<path id="shape-mid"
d="M84.65,131.17L131.42,104.17A18,18 107.83,0 1,156 110.76L156,110.76A18,18 107.83,0 1,149.42 135.35L102.65,162.35A18,18 106.67,0 1,78.06 155.76L78.06,155.76A18,18 106.67,0 1,84.65 131.17z"/>
<path id="shape-bot"
d="M108.65,172.74L108.65,172.74A18,18 116.03,0 1,133.24 179.33L133.24,179.33A18,18 116.03,0 1,126.65 203.92L126.65,203.92A18,18 116.03,0 1,102.06 197.33L102.06,197.33A18,18 116.03,0 1,108.65 172.74z"/>
<mask id="cutout-mask">
<rect x="0" y="0" width="240" height="240" fill="white"/>
<g transform="translate(9.12, 9.12) scale(0.924, 0.924)">
<g transform="translate(45.96154, 43.846153) scale(0.63461536, 0.63461536)">
<use href="#shape-top" fill="black" stroke="black" stroke-width="25"/>
<use href="#shape-mid" fill="black" stroke="black" stroke-width="25"/>
<use href="#shape-bot" fill="black" stroke="black" stroke-width="25"/>
</g>
</g>
</mask>
<linearGradient id="ring-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#B8C0CD"/>
<stop offset="100%" stop-color="#D7DCE6"/>
</linearGradient>
</defs>
<circle cx="120" cy="120" r="56" fill="none" stroke="url(#ring-grad)" stroke-width="16" mask="url(#cutout-mask)"
opacity="0.9"/>
<g transform="translate(9.12, 9.12) scale(0.924, 0.924)" filter="url(#glow-shadow)">
<g transform="translate(45.96154, 43.846153) scale(0.63461536, 0.63461536)">
<use href="#shape-top" fill="#7E8798"/>
<use href="#shape-mid" fill="#687386"/>
<use href="#shape-bot" fill="#98A3B6"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="108" height="108" viewBox="54 54 132 132" xmlns="http://www.w3.org/2000/svg">
<title>status_2</title>
<defs>
<filter id="glow-shadow" x="-50%" y="-50%" width="200%" height="200%">
<feDropShadow dx="0" dy="5" stdDeviation="5" flood-color="#0F172A" flood-opacity="0.12"/>
</filter>
<path id="shape-top" d="M60.65,89.6L154.18,35.6A18,18 107.59,0 1,178.77 42.19L178.77,42.19A18,18 107.59,0 1,172.18 66.78L78.65,120.78A18,18 106.67,0 1,54.06 114.19L54.06,114.19A18,18 106.67,0 1,60.65 89.6z"/>
<path id="shape-mid" d="M84.65,131.17L131.42,104.17A18,18 107.83,0 1,156 110.76L156,110.76A18,18 107.83,0 1,149.42 135.35L102.65,162.35A18,18 106.67,0 1,78.06 155.76L78.06,155.76A18,18 106.67,0 1,84.65 131.17z"/>
<path id="shape-bot" d="M108.65,172.74L108.65,172.74A18,18 116.03,0 1,133.24 179.33L133.24,179.33A18,18 116.03,0 1,126.65 203.92L126.65,203.92A18,18 116.03,0 1,102.06 197.33L102.06,197.33A18,18 116.03,0 1,108.65 172.74z"/>
<mask id="cutout-mask">
<rect x="0" y="0" width="240" height="240" fill="white"/>
<g transform="translate(15.77, 15.77) scale(0.86856, 0.86856)">
<g transform="translate(45.96154, 43.846153) scale(0.63461536, 0.63461536)">
<use href="#shape-top" fill="black" stroke="black" stroke-width="25"/>
<use href="#shape-mid" fill="black" stroke="black" stroke-width="25"/>
<use href="#shape-bot" fill="black" stroke="black" stroke-width="25"/>
</g>
</g>
</mask>
<linearGradient id="ring-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#72B7FF"/>
<stop offset="100%" stop-color="#8066FF"/>
</linearGradient>
</defs>
<circle cx="120" cy="120" r="64" fill="none" stroke="url(#ring-grad)" stroke-width="4" opacity="0.55"/>
<circle cx="120" cy="120" r="54" fill="none" stroke="url(#ring-grad)" stroke-width="12" mask="url(#cutout-mask)" opacity="1"/>
<g transform="translate(15.77, 15.77) scale(0.86856, 0.86856)" filter="url(#glow-shadow)">
<g transform="translate(45.96154, 43.846153) scale(0.63461536, 0.63461536)">
<use href="#shape-top" fill="#6666FB"/>
<use href="#shape-mid" fill="#2F71C4"/>
<use href="#shape-bot" fill="#53B7F2"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="108" height="108" viewBox="54 54 132 132" xmlns="http://www.w3.org/2000/svg">
<title>status_3</title>
<defs>
<filter id="glow-shadow" x="-50%" y="-50%" width="200%" height="200%">
<feDropShadow dx="0" dy="5" stdDeviation="5" flood-color="#0F172A" flood-opacity="0.12"/>
</filter>
<path id="shape-top"
d="M60.65,89.6L154.18,35.6A18,18 107.59,0 1,178.77 42.19L178.77,42.19A18,18 107.59,0 1,172.18 66.78L78.65,120.78A18,18 106.67,0 1,54.06 114.19L54.06,114.19A18,18 106.67,0 1,60.65 89.6z"/>
<path id="shape-mid"
d="M84.65,131.17L131.42,104.17A18,18 107.83,0 1,156 110.76L156,110.76A18,18 107.83,0 1,149.42 135.35L102.65,162.35A18,18 106.67,0 1,78.06 155.76L78.06,155.76A18,18 106.67,0 1,84.65 131.17z"/>
<path id="shape-bot"
d="M108.65,172.74L108.65,172.74A18,18 116.03,0 1,133.24 179.33L133.24,179.33A18,18 116.03,0 1,126.65 203.92L126.65,203.92A18,18 116.03,0 1,102.06 197.33L102.06,197.33A18,18 116.03,0 1,108.65 172.74z"/>
<mask id="cutout-mask">
<rect x="0" y="0" width="240" height="240" fill="white"/>
<g transform="translate(15.77, 15.77) scale(0.86856, 0.86856)">
<g transform="translate(45.96154, 43.846153) scale(0.63461536, 0.63461536)">
<use href="#shape-top" fill="black" stroke="black" stroke-width="25"/>
<use href="#shape-mid" fill="black" stroke="black" stroke-width="25"/>
<use href="#shape-bot" fill="black" stroke="black" stroke-width="25"/>
</g>
</g>
</mask>
<linearGradient id="ring-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#22D3EE"/>
<stop offset="100%" stop-color="#7C5CFF"/>
</linearGradient>
</defs>
<circle cx="120" cy="120" r="64" fill="none" stroke="#FFB02E" stroke-width="4" opacity="0.55"/>
<circle cx="120" cy="120" r="54" fill="none" stroke="url(#ring-grad)" stroke-width="12" mask="url(#cutout-mask)"
opacity="1"/>
<circle cx="120" cy="120" r="42" fill="none" stroke="#FFD25F" stroke-width="3" opacity="0.78"/>
<g transform="translate(15.77, 15.77) scale(0.86856, 0.86856)" filter="url(#glow-shadow)">
<g transform="translate(45.96154, 43.846153) scale(0.63461536, 0.63461536)">
<use href="#shape-top" fill="#7367FF"/>
<use href="#shape-mid" fill="#1685D8"/>
<use href="#shape-bot" fill="#12C8FF"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+4 -12
View File
@@ -129,19 +129,11 @@ func handleAction(action *Action, result ActionResult) {
case getExternalProviderMethod:
externalProviderName := action.Data.(string)
result.success(handleGetExternalProvider(externalProviderName))
return
case updateGeoDataMethod:
paramsString := action.Data.(string)
var params = map[string]string{}
err := json.Unmarshal([]byte(paramsString), &params)
if err != nil {
result.success(err.Error())
return
}
geoType := params["geo-type"]
geoName := params["geo-name"]
handleUpdateGeoData(geoType, geoName, func(value string) {
result.success(value)
})
geoType := action.Data.(string)
handleUpdateGeoData(geoType)
result.success("")
return
case updateExternalProviderMethod:
providerName := action.Data.(string)
+19 -4
View File
@@ -6,6 +6,11 @@ import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"sync"
"github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/adapter/inbound"
"github.com/metacubex/mihomo/adapter/outboundgroup"
@@ -13,6 +18,7 @@ import (
"github.com/metacubex/mihomo/common/batch"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/resolver"
"github.com/metacubex/mihomo/component/updater"
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
@@ -24,10 +30,6 @@ import (
"github.com/metacubex/mihomo/log"
rp "github.com/metacubex/mihomo/rules/provider"
"github.com/metacubex/mihomo/tunnel"
"os"
"path/filepath"
"runtime"
"sync"
)
var (
@@ -234,7 +236,17 @@ func updateConfig(params *UpdateParams) {
general.Tun.Stack = *params.Tun.Stack
}
if params.GeoAutoUpdate != nil {
updater.SetGeoAutoUpdate(*params.GeoAutoUpdate)
}
if params.GeoUpdateInterval != nil {
updater.SetGeoUpdateInterval(*params.GeoUpdateInterval)
}
updateListeners()
if updater.GeoAutoUpdate() {
updater.RegisterGeoUpdaterWithCancel()
}
}
func applyConfig(params *SetupParams) error {
@@ -250,6 +262,9 @@ func applyConfig(params *SetupParams) error {
hub.ApplyConfig(currentConfig)
patchSelectGroup(params.SelectedMap)
updateListeners()
if updater.GeoAutoUpdate() {
updater.RegisterGeoUpdaterWithCancel()
}
return err
}
+14 -4
View File
@@ -34,6 +34,8 @@ type UpdateParams struct {
ExternalController *string `json:"external-controller"`
Interface *string `json:"interface-name"`
UnifiedDelay *bool `json:"unified-delay"`
GeoAutoUpdate *bool `json:"geo-auto-update"`
GeoUpdateInterval *int `json:"geo-update-interval"`
}
type tunSchema struct {
@@ -123,12 +125,20 @@ type Message struct {
}
const (
LogMessage MessageType = "log"
DelayMessage MessageType = "delay"
RequestMessage MessageType = "request"
LoadedMessage MessageType = "loaded"
LogMessage MessageType = "log"
DelayMessage MessageType = "delay"
RequestMessage MessageType = "request"
LoadedMessage MessageType = "loaded"
GeoUpdateMessage MessageType = "geoUpdate"
)
type GeoUpdateStatus struct {
Type string `json:"type"`
Updating bool `json:"updating"`
Skipped bool `json:"skipped,omitempty"`
Error string `json:"error,omitempty"`
}
func (message *Message) Json() (string, error) {
data, err := json.Marshal(message)
return string(data), err
+20 -12
View File
@@ -17,13 +17,15 @@ require (
github.com/ajg/form v1.5.1 // indirect
github.com/akutz/memconn v0.1.0 // indirect
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/coreos/go-iptables v0.8.0 // indirect
github.com/dlclark/regexp2 v1.12.0 // indirect
github.com/dunglas/httpsfv v1.0.2 // indirect
github.com/enfein/mieru/v3 v3.31.0 // indirect
github.com/enfein/mieru/v3 v3.33.0 // indirect
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
@@ -42,6 +44,7 @@ require (
github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect
github.com/josharian/native v1.1.0 // indirect
@@ -51,6 +54,7 @@ require (
github.com/klauspost/reedsolomon v1.12.3 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/metacubex/age v0.0.0-20260603010618-28d156b4ea78 // indirect
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d // indirect
github.com/metacubex/ascon v0.1.0 // indirect
github.com/metacubex/bart v0.26.0 // indirect
@@ -67,30 +71,31 @@ require (
github.com/metacubex/hkdf v0.1.0 // indirect
github.com/metacubex/hpke v0.1.0 // indirect
github.com/metacubex/http v0.1.6 // indirect
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c // indirect
github.com/metacubex/jsonv2 v0.0.0-20260518173308-f4597c22f1df // indirect
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 // indirect
github.com/metacubex/mhurl v0.1.0 // indirect
github.com/metacubex/mlkem v0.1.0 // indirect
github.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb // indirect
github.com/metacubex/qpack v0.6.0 // indirect
github.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306 // indirect
github.com/metacubex/quic-go v0.59.1-0.20260520020949-fcd18c7b6ace // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/restls-client-go v0.1.7 // indirect
github.com/metacubex/sevenzip v1.6.4 // indirect
github.com/metacubex/sing v0.5.7 // indirect
github.com/metacubex/sing-mux v0.3.9 // indirect
github.com/metacubex/sing-quic v0.0.0-20260512151354-8475655be853 // indirect
github.com/metacubex/sing-quic v0.0.0-20260527143057-68e10a6afdc3 // indirect
github.com/metacubex/sing-shadowsocks v0.2.12 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.7 // indirect
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect
github.com/metacubex/sing-tun v0.4.18 // indirect
github.com/metacubex/sing-shadowtls v0.0.0-20260517015314-c11c36474edc // indirect
github.com/metacubex/sing-tun v0.4.20 // indirect
github.com/metacubex/sing-vmess v0.2.5 // indirect
github.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947 // indirect
github.com/metacubex/sing-wireguard v0.0.0-20260520151737-7e7c7c1b854c // indirect
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 // indirect
github.com/metacubex/ssh v0.1.0 // indirect
github.com/metacubex/tailscale v0.0.0-20260516120020-a21c2c99dcbe // indirect
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04 // indirect
github.com/metacubex/tailscale v0.0.0-20260520011538-f23132fac4b7 // indirect
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260521124654-e1bf77ef79af // indirect
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 // indirect
github.com/metacubex/tls v0.1.5 // indirect
github.com/metacubex/tls v0.1.6 // indirect
github.com/metacubex/utls v1.8.4 // indirect
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f // indirect
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect
@@ -100,8 +105,9 @@ require (
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/openacid/low v0.1.21 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pierrec/lz4/v4 v4.1.27 // indirect
github.com/pires/go-proxyproto v0.8.0 // indirect
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/samber/lo v1.53.0 // indirect
@@ -109,11 +115,13 @@ require (
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/stangelandcl/ppmd v0.1.1 // indirect
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
+55 -24
View File
@@ -12,11 +12,15 @@ github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
@@ -30,8 +34,8 @@ github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz
github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dunglas/httpsfv v1.0.2 h1:iERDp/YAfnojSDJ7PW3dj1AReJz4MrwbECSSE59JWL0=
github.com/dunglas/httpsfv v1.0.2/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=
github.com/enfein/mieru/v3 v3.31.0 h1:Fl2ocRCRXJzMygzdRjBHgqI996ZuIDHUmyQyovSf9sA=
github.com/enfein/mieru/v3 v3.31.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/enfein/mieru/v3 v3.33.0 h1:hv2jK8nqYHwpSG86U2rpZR2I8Aff1/J3ifRmd9NBbFc=
github.com/enfein/mieru/v3 v3.33.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
@@ -75,6 +79,8 @@ github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQu
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
@@ -90,10 +96,16 @@ github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/4
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/reedsolomon v1.12.3 h1:tzUznbfc3OFwJaTebv/QdhnFf2Xvb7gZ24XaHLBPmdc=
github.com/klauspost/reedsolomon v1.12.3/go.mod h1:3K5rXwABAvzGeR01r6pWZieUALXO/Tq7bFKGIb4m4WI=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/metacubex/age v0.0.0-20260603010618-28d156b4ea78 h1:LqWr0vb9zDNuQS+jJd4fnRYk/SEI7KJ7TDe/L4WFK48=
github.com/metacubex/age v0.0.0-20260603010618-28d156b4ea78/go.mod h1:BTBG/iVY7rg3qq5WdVCg0GFk58CSvCDSbjy8I7kEx/c=
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d h1:vAJ0ZT4aO803F1uw2roIA9yH7Sxzox34tVVyye1bz6c=
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=
github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM=
@@ -126,8 +138,8 @@ github.com/metacubex/hpke v0.1.0 h1:gu2jUNhraehWi0P/z5HX2md3d7L1FhPQE6/Q0E9r9xQ=
github.com/metacubex/hpke v0.1.0/go.mod h1:vfDm6gfgrwlXUxKDkWbcE44hXtmc1uxLDm2BcR11b3U=
github.com/metacubex/http v0.1.6 h1:xvXuvXMCMxCWMF5nEJF4yiKvXL+p2atWMzs37e80m1I=
github.com/metacubex/http v0.1.6/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c h1:KGhBHDe6FveU0ury+9RyX329nclM1CHODa0Fi+uOAYM=
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c/go.mod h1:F4sVXat6QjPXkNsKRDyyG3BhSkxPFFnRPEIwmmyCgbg=
github.com/metacubex/jsonv2 v0.0.0-20260518173308-f4597c22f1df h1:S0vBzqjXok24VopstOgPd1JdgglW9tXehrqvwpQWbQ8=
github.com/metacubex/jsonv2 v0.0.0-20260518173308-f4597c22f1df/go.mod h1:F4sVXat6QjPXkNsKRDyyG3BhSkxPFFnRPEIwmmyCgbg=
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=
github.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=
@@ -138,42 +150,44 @@ github.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb h1:wk6mHYPURSUv
github.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb/go.mod h1:73ZrCfhdkW4F2E2GAlta3km/S2RHhFNogCMtWZV2anQ=
github.com/metacubex/qpack v0.6.0 h1:YqClGIMOpiRYLjV1qOs483Od08MdPgRnHjt90FuaAKw=
github.com/metacubex/qpack v0.6.0/go.mod h1:lKGSi7Xk94IMvHGOmxS9eIei3bvIqpOAImEBsaOwTkA=
github.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306 h1:HlGLmLsWJMLSu0CMI9z/BmEnithB4oXM5Rom6/0Qxtg=
github.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306/go.mod h1:oNzMrmylS897M3zSMuapIdwSwfq6F2qW01Z3NhVRJhk=
github.com/metacubex/quic-go v0.59.1-0.20260520020949-fcd18c7b6ace h1:KXacx7dp1GYVMgxezwXRt5BMsEbvAYuA6rPFUmdAvcQ=
github.com/metacubex/quic-go v0.59.1-0.20260520020949-fcd18c7b6ace/go.mod h1:2YEQEvFrZ5V76oynMBDTlN+4fdnSHCa2uNJxv3cm1HU=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=
github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g=
github.com/metacubex/sevenzip v1.6.4 h1:OIL+DeOeSAbKNsjqxcYUMiarRmX6Kaxakb0GT7E9Oik=
github.com/metacubex/sevenzip v1.6.4/go.mod h1:FP3X9bzFKj9wPxifGN9B3w2fIEicMjzKYIGIhnu+1pw=
github.com/metacubex/sing v0.5.7 h1:8OC+fhKFSv/l9ehEhJRaZZAOuthfZo68SteBVLe8QqM=
github.com/metacubex/sing v0.5.7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.9 h1:/aoBD2+sK2qsXDlNDe3hkR0GZuFDtwIZhOeGUx9W0Yk=
github.com/metacubex/sing-mux v0.3.9/go.mod h1:8bT7ZKT3clRrJjYc/x5CRYibC1TX/bK73a3r3+2E+Fc=
github.com/metacubex/sing-quic v0.0.0-20260512151354-8475655be853 h1:nZ5WNU6kjj6kBu4+2eMySFkUVGCop64rZnLMm+HPh8w=
github.com/metacubex/sing-quic v0.0.0-20260512151354-8475655be853/go.mod h1:6ayFGfzzBE85csgQkM3gf4neFq6s0losHlPRSxY+nuk=
github.com/metacubex/sing-quic v0.0.0-20260527143057-68e10a6afdc3 h1:PnMby5+kZXTl/CFDHfxMbMTaSRD+uMKMsrDYVQyAmX8=
github.com/metacubex/sing-quic v0.0.0-20260527143057-68e10a6afdc3/go.mod h1:6ayFGfzzBE85csgQkM3gf4neFq6s0losHlPRSxY+nuk=
github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=
github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=
github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A=
github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.18 h1:WRzAosG0YkT3aZq5RJWtF+RdCgeJ8EpooS5ZM1lkXo0=
github.com/metacubex/sing-tun v0.4.18/go.mod h1:g4I/JNplDBhXLF+aQWgFbhNeJPSXQOWS9HvLeNvkgeA=
github.com/metacubex/sing-shadowtls v0.0.0-20260517015314-c11c36474edc h1:8wLoFfYQ88iGPL+krQ5tJsI8IAmkFjKpQL2q+y3pvss=
github.com/metacubex/sing-shadowtls v0.0.0-20260517015314-c11c36474edc/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.20 h1:xdupzizRoZKyDzP0l68WAx5Sk4ooiuT1GiWsiJyOGPw=
github.com/metacubex/sing-tun v0.4.20/go.mod h1:g4I/JNplDBhXLF+aQWgFbhNeJPSXQOWS9HvLeNvkgeA=
github.com/metacubex/sing-vmess v0.2.5 h1:m9Zt5I27lB9fmLMZfism9sH2LcnAfShZfwSkf6/KJoE=
github.com/metacubex/sing-vmess v0.2.5/go.mod h1:AwtlzUgf8COe9tRYAKqWZ+leDH7p5U98a0ZUpYehl8Q=
github.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947 h1:IB03BvRQtvjWScyOK5jSQVJYY8osmZXHL+4VCEFMWcM=
github.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
github.com/metacubex/sing-wireguard v0.0.0-20260520151737-7e7c7c1b854c h1:tH9FuQW357zp2xAGzkoZTGpNGMVmEFZov0iV5M2S5ew=
github.com/metacubex/sing-wireguard v0.0.0-20260520151737-7e7c7c1b854c/go.mod h1:eQZDJTx+IH3k4mXqaOJ3VJ9h9ZqOl60F7TLi5wAU51Q=
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2BhiAPgbJygiWhesPlfGmF+9Vw6ARdk=
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=
github.com/metacubex/ssh v0.1.0 h1:iGfr99qk/eMHzUnQ/0bTxXT8+8SWqLSHBWDHoAhngzw=
github.com/metacubex/ssh v0.1.0/go.mod h1:NUtl0d+/f2cG9ECEpMM8iCVOpmggQlC13oLeDUONDlU=
github.com/metacubex/tailscale v0.0.0-20260516120020-a21c2c99dcbe h1:ZdAKshacNruZGuKTE8WMTuxyGgpv/LySLoE/EEmgF9c=
github.com/metacubex/tailscale v0.0.0-20260516120020-a21c2c99dcbe/go.mod h1:2G1V82OGXgxT7m7046GA80I9SlcvczljCK0C7NQ3c10=
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04 h1:zk+mDDSBl5lv80WWtaFUbpj8XLb7AhjCUbn2pB37N0U=
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04/go.mod h1:pKUKBy7IcQ5r0i66gWENHgxKvBn8tlgAGx0DZMq8h5M=
github.com/metacubex/tailscale v0.0.0-20260520011538-f23132fac4b7 h1:LoJR4NMyNKHeEJoeGDtcsao7sV0NRkzMeV5H/0J0MIE=
github.com/metacubex/tailscale v0.0.0-20260520011538-f23132fac4b7/go.mod h1:MAo3HhE7968rIwmDvYTYE8xCsV4x+hLnkChdXeP3X4c=
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260521124654-e1bf77ef79af h1:c60IbBMUq2h1M2m7+grMJJmBmrObxL8SwvNtm6Ozbwk=
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260521124654-e1bf77ef79af/go.mod h1:i3zLKytWkOnyT1i9OmiLevWvrN5J5HE1+yjE7UYNfcQ=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/tls v0.1.5 h1:ECcB83dj+zadnhlKcLnUUf1Sq6+vU0f/zoyU0+9oPTc=
github.com/metacubex/tls v0.1.5/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/tls v0.1.6 h1:t2ubLneYa4ceyIC++54a57BLqZFA/QYUrhdjLk2GPwo=
github.com/metacubex/tls v0.1.6/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
@@ -195,13 +209,16 @@ github.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs
github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e h1:dCWirM5F3wMY+cmRda/B1BiPsFtmzXqV9b0hLWtVBMs=
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e/go.mod h1:9leZcVcItj6m9/CfHY5Em/iBrCz7js8LcRQGTKEEv2M=
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
@@ -216,10 +233,19 @@ github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIF
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/stangelandcl/ppmd v0.1.1 h1:c25QazhlWUn5nmR1QOzafKhQxBicAr7GGCKER2aJ8H8=
github.com/stangelandcl/ppmd v0.1.1/go.mod h1:Rrv7M+/2P5jYr/GMLhBl7Ug3uJ1bUiVzr5LbbaV6xgY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d h1:JcGKBZAL7ePLwOhUdN8qGQZlP5GueEiIZwY7R62pejE=
@@ -232,6 +258,8 @@ github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+y
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
@@ -245,6 +273,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiEYzdMlNsVvBYOAJWZjdktqFE9tQh5BT2+4=
@@ -302,8 +332,9 @@ golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+23 -23
View File
@@ -347,36 +347,22 @@ func handleGetExternalProvider(externalProviderName string) string {
return string(data)
}
func handleUpdateGeoData(geoType string, geoName string, fn func(value string)) {
func handleUpdateGeoData(geoType string) {
go func() {
path := constant.Path.Resolve(geoName)
switch geoType {
case "MMDB":
err := updater.UpdateMMDBWithPath(path)
if err != nil {
fn(err.Error())
return
}
updater.UpdateMMDB()
return
case "ASN":
err := updater.UpdateASNWithPath(path)
if err != nil {
fn(err.Error())
return
}
updater.UpdateASN()
return
case "GEOIP":
err := updater.UpdateGeoIpWithPath(path)
if err != nil {
fn(err.Error())
return
}
updater.UpdateGeoIp()
return
case "GEOSITE":
err := updater.UpdateGeoSiteWithPath(path)
if err != nil {
fn(err.Error())
return
}
updater.UpdateGeoSite()
return
}
fn("")
}()
}
@@ -568,4 +554,18 @@ func init() {
Data: providerName,
})
}
updater.GeoUpdateHook = func(geoType string, updating bool, skipped bool, updateErr error) {
status := GeoUpdateStatus{
Type: geoType,
Updating: updating,
Skipped: skipped,
}
if updateErr != nil {
status.Error = updateErr.Error()
}
sendMessage(Message{
Type: GeoUpdateMessage,
Data: status,
})
}
}
+1 -1
View File
@@ -12,6 +12,7 @@ export 'future.dart';
export 'http.dart';
export 'icons.dart';
export 'indexing.dart';
export 'input_limits.dart';
export 'iterable.dart';
export 'javascript.dart';
export 'keyboard.dart';
@@ -29,7 +30,6 @@ export 'package.dart';
export 'path.dart';
export 'permission.dart';
export 'picker.dart';
export 'pipe.dart';
export 'preferences.dart';
export 'print.dart';
export 'protocol.dart';
+4 -12
View File
@@ -7,7 +7,7 @@ class FixedList<T> {
final List<T> _list;
FixedList(this.maxLength, {List<T>? list})
: _list = (list ?? [])..truncate(maxLength);
: _list = (list ?? [])..truncate(maxLength);
void add(T item) {
_list.add(item);
@@ -25,10 +25,7 @@ class FixedList<T> {
T operator [](int index) => _list[index];
FixedList<T> copyWith() {
return FixedList(
maxLength,
list: List.of(_list),
);
return FixedList(maxLength, list: List.of(_list));
}
}
@@ -41,10 +38,7 @@ class FixedMap<K, V> {
}
V updateCacheValue(K key, ValueCallback<V> callback) {
final realValue = _map.updateCacheValue(
key,
callback,
);
final realValue = _map.updateCacheValue(key, callback);
_adjustMap();
return realValue;
}
@@ -65,9 +59,7 @@ class FixedMap<K, V> {
void _adjustMap() {
if (_map.length > maxLength) {
_map = Map.fromEntries(
map.entries.toList()..truncate(maxLength),
);
_map = Map.fromEntries(map.entries.toList()..truncate(maxLength));
}
}
+38
View File
@@ -0,0 +1,38 @@
import 'package:flutter/services.dart';
abstract final class TextInputLimits {
static const name = 64;
static const groupName = 64;
static const url = 2048;
static const uri = url;
static const iconUrl = url;
static const dnsServer = url;
static const hostValue = url;
static const userName = 128;
static const password = 512;
static const userAgent = 512;
static const fileName = 255;
static const port = 5;
static const number = 10;
static const interval = number;
static const search = 256;
static const rule = 1024;
static const filter = 1024;
static const status = 128;
static const dnsListen = 255;
static const domain = 512;
static const geoSite = 128;
static const geoIpCode = 16;
static const cidr = 64;
static List<TextInputFormatter> limit(int maxLength) {
return [LengthLimitingTextInputFormatter(maxLength)];
}
static List<TextInputFormatter> digitsOnly(int maxLength) {
return [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(maxLength),
];
}
}
+7
View File
@@ -80,6 +80,13 @@ extension ListExt<T> on List<T> {
return newList;
}
List<T> copyAndReorder(int oldIndex, int newIndex) {
final newList = List<T>.from(this);
final item = newList.removeAt(oldIndex);
newList.insert(newIndex, item);
return newList;
}
List<T> safeSublist(int start, [int? end]) {
if (start <= 0) return this;
if (start > length) return [];
+84 -84
View File
@@ -5,90 +5,90 @@ import 'system.dart';
final Map<PhysicalKeyboardKey, String> _knownKeyLabels =
<PhysicalKeyboardKey, String>{
PhysicalKeyboardKey.keyA: 'A',
PhysicalKeyboardKey.keyB: 'B',
PhysicalKeyboardKey.keyC: 'C',
PhysicalKeyboardKey.keyD: 'D',
PhysicalKeyboardKey.keyE: 'E',
PhysicalKeyboardKey.keyF: 'F',
PhysicalKeyboardKey.keyG: 'G',
PhysicalKeyboardKey.keyH: 'H',
PhysicalKeyboardKey.keyI: 'I',
PhysicalKeyboardKey.keyJ: 'J',
PhysicalKeyboardKey.keyK: 'K',
PhysicalKeyboardKey.keyL: 'L',
PhysicalKeyboardKey.keyM: 'M',
PhysicalKeyboardKey.keyN: 'N',
PhysicalKeyboardKey.keyO: 'O',
PhysicalKeyboardKey.keyP: 'P',
PhysicalKeyboardKey.keyQ: 'Q',
PhysicalKeyboardKey.keyR: 'R',
PhysicalKeyboardKey.keyS: 'S',
PhysicalKeyboardKey.keyT: 'T',
PhysicalKeyboardKey.keyU: 'U',
PhysicalKeyboardKey.keyV: 'V',
PhysicalKeyboardKey.keyW: 'W',
PhysicalKeyboardKey.keyX: 'X',
PhysicalKeyboardKey.keyY: 'Y',
PhysicalKeyboardKey.keyZ: 'Z',
PhysicalKeyboardKey.digit1: '1',
PhysicalKeyboardKey.digit2: '2',
PhysicalKeyboardKey.digit3: '3',
PhysicalKeyboardKey.digit4: '4',
PhysicalKeyboardKey.digit5: '5',
PhysicalKeyboardKey.digit6: '6',
PhysicalKeyboardKey.digit7: '7',
PhysicalKeyboardKey.digit8: '8',
PhysicalKeyboardKey.digit9: '9',
PhysicalKeyboardKey.digit0: '0',
PhysicalKeyboardKey.enter: 'ENTER',
PhysicalKeyboardKey.escape: 'ESCAPE',
PhysicalKeyboardKey.backspace: 'BACKSPACE',
PhysicalKeyboardKey.tab: 'TAB',
PhysicalKeyboardKey.space: 'SPACE',
PhysicalKeyboardKey.minus: '-',
PhysicalKeyboardKey.equal: '=',
PhysicalKeyboardKey.bracketLeft: '[',
PhysicalKeyboardKey.bracketRight: ']',
PhysicalKeyboardKey.backslash: '\\',
PhysicalKeyboardKey.semicolon: ';',
PhysicalKeyboardKey.quote: '"',
PhysicalKeyboardKey.backquote: '`',
PhysicalKeyboardKey.comma: ',',
PhysicalKeyboardKey.period: '.',
PhysicalKeyboardKey.slash: '/',
PhysicalKeyboardKey.capsLock: 'CAPSLOCK',
PhysicalKeyboardKey.f1: 'F1',
PhysicalKeyboardKey.f2: 'F2',
PhysicalKeyboardKey.f3: 'F3',
PhysicalKeyboardKey.f4: 'F4',
PhysicalKeyboardKey.f5: 'F5',
PhysicalKeyboardKey.f6: 'F6',
PhysicalKeyboardKey.f7: 'F7',
PhysicalKeyboardKey.f8: 'F8',
PhysicalKeyboardKey.f9: 'F9',
PhysicalKeyboardKey.f10: 'F10',
PhysicalKeyboardKey.f11: 'F11',
PhysicalKeyboardKey.f12: 'F12',
PhysicalKeyboardKey.home: 'HOME',
PhysicalKeyboardKey.pageUp: 'PAGEUP',
PhysicalKeyboardKey.delete: 'DELETE',
PhysicalKeyboardKey.end: 'END',
PhysicalKeyboardKey.pageDown: 'PAGEDOWN',
PhysicalKeyboardKey.arrowRight: '→',
PhysicalKeyboardKey.arrowLeft: '←',
PhysicalKeyboardKey.arrowDown: '↓',
PhysicalKeyboardKey.arrowUp: '↑',
PhysicalKeyboardKey.controlLeft: 'CTRL',
PhysicalKeyboardKey.shiftLeft: 'SHIFT',
PhysicalKeyboardKey.altLeft: 'ALT',
PhysicalKeyboardKey.metaLeft: system.isMacOS ? '⌘' : 'WIN',
PhysicalKeyboardKey.controlRight: 'CTRL',
PhysicalKeyboardKey.shiftRight: 'SHIFT',
PhysicalKeyboardKey.altRight: 'ALT',
PhysicalKeyboardKey.metaRight: system.isMacOS ? '⌘' : 'WIN',
PhysicalKeyboardKey.fn: 'FN',
};
PhysicalKeyboardKey.keyA: 'A',
PhysicalKeyboardKey.keyB: 'B',
PhysicalKeyboardKey.keyC: 'C',
PhysicalKeyboardKey.keyD: 'D',
PhysicalKeyboardKey.keyE: 'E',
PhysicalKeyboardKey.keyF: 'F',
PhysicalKeyboardKey.keyG: 'G',
PhysicalKeyboardKey.keyH: 'H',
PhysicalKeyboardKey.keyI: 'I',
PhysicalKeyboardKey.keyJ: 'J',
PhysicalKeyboardKey.keyK: 'K',
PhysicalKeyboardKey.keyL: 'L',
PhysicalKeyboardKey.keyM: 'M',
PhysicalKeyboardKey.keyN: 'N',
PhysicalKeyboardKey.keyO: 'O',
PhysicalKeyboardKey.keyP: 'P',
PhysicalKeyboardKey.keyQ: 'Q',
PhysicalKeyboardKey.keyR: 'R',
PhysicalKeyboardKey.keyS: 'S',
PhysicalKeyboardKey.keyT: 'T',
PhysicalKeyboardKey.keyU: 'U',
PhysicalKeyboardKey.keyV: 'V',
PhysicalKeyboardKey.keyW: 'W',
PhysicalKeyboardKey.keyX: 'X',
PhysicalKeyboardKey.keyY: 'Y',
PhysicalKeyboardKey.keyZ: 'Z',
PhysicalKeyboardKey.digit1: '1',
PhysicalKeyboardKey.digit2: '2',
PhysicalKeyboardKey.digit3: '3',
PhysicalKeyboardKey.digit4: '4',
PhysicalKeyboardKey.digit5: '5',
PhysicalKeyboardKey.digit6: '6',
PhysicalKeyboardKey.digit7: '7',
PhysicalKeyboardKey.digit8: '8',
PhysicalKeyboardKey.digit9: '9',
PhysicalKeyboardKey.digit0: '0',
PhysicalKeyboardKey.enter: 'ENTER',
PhysicalKeyboardKey.escape: 'ESCAPE',
PhysicalKeyboardKey.backspace: 'BACKSPACE',
PhysicalKeyboardKey.tab: 'TAB',
PhysicalKeyboardKey.space: 'SPACE',
PhysicalKeyboardKey.minus: '-',
PhysicalKeyboardKey.equal: '=',
PhysicalKeyboardKey.bracketLeft: '[',
PhysicalKeyboardKey.bracketRight: ']',
PhysicalKeyboardKey.backslash: '\\',
PhysicalKeyboardKey.semicolon: ';',
PhysicalKeyboardKey.quote: '"',
PhysicalKeyboardKey.backquote: '`',
PhysicalKeyboardKey.comma: ',',
PhysicalKeyboardKey.period: '.',
PhysicalKeyboardKey.slash: '/',
PhysicalKeyboardKey.capsLock: 'CAPSLOCK',
PhysicalKeyboardKey.f1: 'F1',
PhysicalKeyboardKey.f2: 'F2',
PhysicalKeyboardKey.f3: 'F3',
PhysicalKeyboardKey.f4: 'F4',
PhysicalKeyboardKey.f5: 'F5',
PhysicalKeyboardKey.f6: 'F6',
PhysicalKeyboardKey.f7: 'F7',
PhysicalKeyboardKey.f8: 'F8',
PhysicalKeyboardKey.f9: 'F9',
PhysicalKeyboardKey.f10: 'F10',
PhysicalKeyboardKey.f11: 'F11',
PhysicalKeyboardKey.f12: 'F12',
PhysicalKeyboardKey.home: 'HOME',
PhysicalKeyboardKey.pageUp: 'PAGEUP',
PhysicalKeyboardKey.delete: 'DELETE',
PhysicalKeyboardKey.end: 'END',
PhysicalKeyboardKey.pageDown: 'PAGEDOWN',
PhysicalKeyboardKey.arrowRight: '→',
PhysicalKeyboardKey.arrowLeft: '←',
PhysicalKeyboardKey.arrowDown: '↓',
PhysicalKeyboardKey.arrowUp: '↑',
PhysicalKeyboardKey.controlLeft: 'CTRL',
PhysicalKeyboardKey.shiftLeft: 'SHIFT',
PhysicalKeyboardKey.altLeft: 'ALT',
PhysicalKeyboardKey.metaLeft: system.isMacOS ? '⌘' : 'WIN',
PhysicalKeyboardKey.controlRight: 'CTRL',
PhysicalKeyboardKey.shiftRight: 'SHIFT',
PhysicalKeyboardKey.altRight: 'ALT',
PhysicalKeyboardKey.metaRight: system.isMacOS ? '⌘' : 'WIN',
PhysicalKeyboardKey.fn: 'FN',
};
extension KeyboardKeyExt on KeyboardKey {
String get label {
+11 -12
View File
@@ -16,21 +16,20 @@ class LinkManager {
}
Future<void> initAppLinksListen(
Function(String url) installConfigCallBack) async {
Function(String url) installConfigCallBack,
) async {
commonPrint.log('initAppLinksListen');
destroy();
subscription = _appLinks.uriLinkStream.listen(
(uri) {
commonPrint.log('onAppLink: $uri');
if (uri.host == 'install-config') {
final parameters = uri.queryParameters;
final url = parameters['url'];
if (url != null) {
installConfigCallBack(url);
}
subscription = _appLinks.uriLinkStream.listen((uri) {
commonPrint.log('onAppLink: $uri');
if (uri.host == 'install-config') {
final parameters = uri.queryParameters;
final url = parameters['url'];
if (url != null) {
installConfigCallBack(url);
}
},
);
}
});
}
void destroy() {
+4 -4
View File
@@ -6,8 +6,8 @@ import 'common.dart';
extension PackageInfoExtension on PackageInfo {
String get ua => [
'$appName/v$version',
'clash-verge',
'Platform/${Platform.operatingSystem}',
].join(' ');
'$appName/v$version',
'clash-verge',
'Platform/${Platform.operatingSystem}',
].join(' ');
}
+9 -11
View File
@@ -7,13 +7,8 @@ import 'package:image_picker/image_picker.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
class Picker {
Future<PlatformFile?> pickerFile({bool withData = true}) async {
final filePickerResult = await FilePicker.pickFiles(
withData: withData,
allowMultiple: false,
initialDirectory: await appPath.downloadDirPath,
);
return filePickerResult?.files.first;
Future<PlatformFile?> pickerFile() async {
return FilePicker.pickFile(initialDirectory: await appPath.downloadDirPath);
}
Future<String?> saveFile(String fileName, Uint8List bytes) async {
@@ -34,15 +29,12 @@ class Picker {
if (!await localFile.exists()) {
await localFile.create(recursive: true);
}
final bytes = Platform.isAndroid ? await localFile.readAsBytes() : null;
final bytes = await localFile.readAsBytes();
final path = await FilePicker.saveFile(
fileName: fileName,
initialDirectory: await appPath.downloadDirPath,
bytes: bytes,
);
if (path != null && bytes == null) {
await localFile.copy(path);
}
await localFile.safeDelete();
return path;
}
@@ -65,4 +57,10 @@ class Picker {
}
}
extension PlatformFileExt on PlatformFile {
Future<Uint8List> readBytes() {
return readAsBytes();
}
}
final picker = Picker();
-234
View File
@@ -1,234 +0,0 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:win32/win32.dart';
const _pipeBufferSize = 256 * 1024;
class _PipeCmd {
final int type; // 0 = write, 1 = shutdown
final Uint8List? data;
const _PipeCmd(this.type, this.data);
}
void _pipeIsolate(List<Object> args) {
final pipeName = args[0] as String;
final dataPort = args[1] as SendPort;
final namePtr = pipeName.toNativeUtf16();
final handle = CreateNamedPipe(
namePtr,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
_pipeBufferSize,
_pipeBufferSize,
0,
nullptr,
);
calloc.free(namePtr);
if (handle == INVALID_HANDLE_VALUE) {
dataPort.send(
'error:CreateNamedPipe failed, Win32 error=${GetLastError()}',
);
return;
}
final writeRx = ReceivePort();
final readBuf = calloc<Uint8>(_pipeBufferSize);
final bytesRead = calloc<Uint32>();
final overlapped = calloc<OVERLAPPED>();
final readEvent = CreateEvent(nullptr, 1, 0, nullptr);
overlapped.ref.hEvent = readEvent;
var running = true;
var clientConnected = false;
writeRx.listen((msg) {
if (msg is _PipeCmd) {
if (msg.type == 0) {
if (clientConnected && msg.data != null) {
final buf = calloc<Uint8>(msg.data!.length);
final written = calloc<Uint32>();
buf.asTypedList(msg.data!.length).setAll(0, msg.data!);
WriteFile(handle, buf, msg.data!.length, written, nullptr);
calloc.free(written);
calloc.free(buf);
}
} else if (msg.type == 1) {
running = false;
if (clientConnected) {
DisconnectNamedPipe(handle);
clientConnected = false;
}
writeRx.close();
}
}
});
final connectEvent = CreateEvent(nullptr, 1, 0, nullptr);
final connectOverlapped = calloc<OVERLAPPED>();
connectOverlapped.ref.hEvent = connectEvent;
while (running) {
final cr = ConnectNamedPipe(handle, connectOverlapped);
if (cr == 0) {
final err = GetLastError();
if (err == ERROR_IO_PENDING) {
while (running) {
final wr = WaitForSingleObject(connectEvent, 100);
if (wr == WAIT_OBJECT_0) break;
}
if (!running) break;
} else if (err != ERROR_PIPE_CONNECTED) {
dataPort.send('error:ConnectNamedPipe failed, Win32 error=$err');
break;
}
}
if (!running) break;
clientConnected = true;
dataPort.send(writeRx.sendPort);
var readPending = false;
while (running && clientConnected) {
if (!readPending) {
bytesRead.value = 0;
final readResult = ReadFile(
handle,
readBuf,
_pipeBufferSize,
bytesRead,
overlapped,
);
if (readResult != 0) {
if (bytesRead.value > 0) {
dataPort.send(
Uint8List.fromList(readBuf.asTypedList(bytesRead.value)),
);
} else {
break;
}
} else {
final err = GetLastError();
if (err == ERROR_IO_PENDING) {
readPending = true;
} else if (bytesRead.value > 0) {
dataPort.send(
Uint8List.fromList(readBuf.asTypedList(bytesRead.value)),
);
} else {
break;
}
}
}
if (readPending) {
final wr = WaitForSingleObject(readEvent, 100);
if (wr == WAIT_OBJECT_0) {
final got = GetOverlappedResult(handle, overlapped, bytesRead, 0);
if (got != 0 && bytesRead.value > 0) {
dataPort.send(
Uint8List.fromList(readBuf.asTypedList(bytesRead.value)),
);
readPending = false;
} else {
break;
}
} else if (wr != WAIT_TIMEOUT) {
break;
}
}
}
if (running && clientConnected) {
DisconnectNamedPipe(handle);
clientConnected = false;
dataPort.send('closed');
}
}
CloseHandle(readEvent);
CloseHandle(connectEvent);
calloc.free(connectOverlapped);
calloc.free(overlapped);
calloc.free(bytesRead);
calloc.free(readBuf);
CloseHandle(handle);
}
class NamedPipeServer {
final String pipeName;
Isolate? _isolate;
SendPort? _writePort;
ReceivePort? _responsePort;
StreamSubscription? _responseSub;
final _dataController = StreamController<Uint8List>.broadcast();
Completer<void> _connectionCompleter = Completer<void>();
void Function()? onDisconnect;
NamedPipeServer._(this.pipeName);
Stream<Uint8List> get dataStream => _dataController.stream;
Completer<void> get connectionCompleter => _connectionCompleter;
static Future<NamedPipeServer> bind(String pipeName) async {
final server = NamedPipeServer._(pipeName);
server._responsePort = ReceivePort();
server._responseSub = server._responsePort!.listen((message) {
if (message is SendPort) {
server._writePort = message;
if (server._connectionCompleter.isCompleted) {
server._connectionCompleter = Completer<void>();
}
server._connectionCompleter.complete();
} else if (message is Uint8List) {
server._dataController.add(message);
} else if (message == 'closed') {
server._connectionCompleter = Completer<void>();
server.onDisconnect?.call();
} else if (message is String && message.startsWith('error:')) {
commonPrint.log('[NamedPipeServer] $message', logLevel: LogLevel.error);
}
});
server._isolate = await Isolate.spawn(_pipeIsolate, [
pipeName,
server._responsePort!.sendPort,
]);
return server;
}
void writeln(String message) {
final wp = _writePort;
if (wp == null) return;
final str = '$message\n';
wp.send(_PipeCmd(0, Uint8List.fromList(str.codeUnits)));
}
Future<void> close() async {
final wp = _writePort;
if (wp == null) return;
_writePort = null;
wp.send(const _PipeCmd(1, null));
_responseSub?.cancel();
_responsePort?.close();
await Future.delayed(const Duration(milliseconds: 50));
_isolate?.kill();
_isolate = null;
}
}
+38 -12
View File
@@ -2,6 +2,26 @@ import 'dart:io';
import 'package:win32_registry/win32_registry.dart';
class ProtocolRegistrationPlan {
final String scheme;
final String executable;
const ProtocolRegistrationPlan({
required this.scheme,
required this.executable,
});
String get protocolKey => 'Software\\Classes\\$scheme';
String get commandKey => 'shell\\open\\command';
String get protocolValueName => 'URL Protocol';
String get protocolValue => '';
String get command => '"$executable" "%1"';
}
class Protocol {
static Protocol? _instance;
@@ -13,19 +33,25 @@ class Protocol {
}
void register(String scheme) {
final String protocolRegKey = 'Software\\Classes\\$scheme';
const RegistryValue protocolRegValue = RegistryValue.string(
'URL Protocol',
'',
final plan = ProtocolRegistrationPlan(
scheme: scheme,
executable: Platform.resolvedExecutable,
);
const String protocolCmdRegKey = 'shell\\open\\command';
final RegistryValue protocolCmdRegValue = RegistryValue.string(
'',
'"${Platform.resolvedExecutable}" "%1"',
);
final regKey = Registry.currentUser.createKey(protocolRegKey);
regKey.createValue(protocolRegValue);
regKey.createKey(protocolCmdRegKey).createValue(protocolCmdRegValue);
final regKey = CURRENT_USER.create(plan.protocolKey);
try {
regKey.setValue(
plan.protocolValueName,
RegistryValue.string(plan.protocolValue),
);
final commandKey = regKey.create(plan.commandKey);
try {
commandKey.setValue('', RegistryValue.string(plan.command));
} finally {
commandKey.close();
}
} finally {
regKey.close();
}
}
}
+30 -30
View File
@@ -113,30 +113,30 @@ class Request {
final future = dio
.get<Map<String, dynamic>>(
source.key,
cancelToken: token,
options: Options(responseType: ResponseType.json),
)
source.key,
cancelToken: token,
options: Options(responseType: ResponseType.json),
)
.timeout(const Duration(seconds: 10));
future
.then((res) {
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
return;
}
commonPrint.log('checkIp data empty', logLevel: LogLevel.info);
failureCount++;
handleFailRes();
})
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
return;
}
commonPrint.log('checkIp data empty', logLevel: LogLevel.info);
failureCount++;
handleFailRes();
})
.catchError((e) {
failureCount++;
if (e is DioException && e.type == DioExceptionType.cancel) {
completer.complete(Result.error('cancelled'));
return;
}
commonPrint.log('checkIp error $e', logLevel: LogLevel.warning);
handleFailRes();
});
failureCount++;
if (e is DioException && e.type == DioExceptionType.cancel) {
completer.complete(Result.error('cancelled'));
return;
}
commonPrint.log('checkIp error $e', logLevel: LogLevel.warning);
handleFailRes();
});
return completer.future;
});
final res = await Future.any(futures);
@@ -149,9 +149,9 @@ class Request {
try {
final response = await dio
.get(
'http://$localhost:$helperPort/ping',
options: Options(responseType: ResponseType.plain),
)
'http://$localhost:$helperPort/ping',
options: Options(responseType: ResponseType.plain),
)
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
@@ -166,10 +166,10 @@ class Request {
try {
final response = await dio
.post(
'http://$localhost:$helperPort/start',
data: json.encode({'path': appPath.corePath, 'arg': arg}),
options: Options(responseType: ResponseType.plain),
)
'http://$localhost:$helperPort/start',
data: json.encode({'path': appPath.corePath, 'arg': arg}),
options: Options(responseType: ResponseType.plain),
)
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
@@ -185,9 +185,9 @@ class Request {
try {
final response = await dio
.post(
'http://$localhost:$helperPort/stop',
options: Options(responseType: ResponseType.plain),
)
'http://$localhost:$helperPort/stop',
options: Options(responseType: ResponseType.plain),
)
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
+1
View File
@@ -104,6 +104,7 @@ class System {
obscureText: true,
title: currentAppLocalizations.pleaseInputAdminPassword,
value: '',
inputFormatters: TextInputLimits.limit(TextInputLimits.password),
),
);
if (password == null || password.isEmpty) {
+5 -5
View File
@@ -183,7 +183,7 @@ Future<VM2<String, String>> _makeRealProfileTask(
}
}
rawConfig['profile']['store-selected'] = false;
rawConfig['geox-url'] = realPatchConfig.geoXUrl.toJson();
rawConfig['geox-url'] = realPatchConfig.geoXUrl.raw;
rawConfig['global-ua'] = realPatchConfig.globalUa ?? defaultUA;
if (rawConfig['hosts'] == null) {
rawConfig['hosts'] = {};
@@ -331,10 +331,10 @@ Future<String> _encodeLogsTask(List<Log> data) async {
Future<MigrationData> oldToNowTask(Map<String, Object?> data) async {
final homeDir = await appPath.homeDirPath;
return compute<
VM3<Map<String, Object?>, String, String>,
MigrationData
>(_oldToNowTask, VM3(data, homeDir, homeDir));
return compute<VM3<Map<String, Object?>, String, String>, MigrationData>(
_oldToNowTask,
VM3(data, homeDir, homeDir),
);
}
Future<MigrationData> _oldToNowTask(
+3 -6
View File
@@ -11,11 +11,8 @@ extension TextStyleExtension on TextStyle {
TextStyle get toBold => copyWith(fontWeight: FontWeight.bold);
TextStyle get toJetBrainsMono => copyWith(
fontFamily: FontFamily.jetBrainsMono.value,
);
TextStyle get toJetBrainsMono =>
copyWith(fontFamily: FontFamily.jetBrainsMono.value);
TextStyle adjustSize(int size) => copyWith(
fontSize: fontSize! + size,
);
TextStyle adjustSize(int size) => copyWith(fontSize: fontSize! + size);
}
+2 -2
View File
@@ -177,8 +177,8 @@ class CoreController {
return ExternalProvider.fromJson(json.decode(externalProvidersRawString));
}
Future<String> updateGeoData(UpdateGeoDataParams params) {
return _interface.updateGeoData(params);
Future<String> updateGeoData(String type) {
return _interface.updateGeoData(type);
}
Future<String> sideLoadExternalProvider({
+16
View File
@@ -14,6 +14,13 @@ abstract mixin class CoreEventListener {
void onLoaded(String providerName) {}
void onCrash(String message) {}
void onGeoUpdate(
String geoType,
bool updating,
bool skipped,
String? error,
) {}
}
class CoreEventManager {
@@ -38,6 +45,15 @@ class CoreEventManager {
case CoreEventType.crash:
listener.onCrash(event.data);
break;
case CoreEventType.geoUpdate:
final data = event.data as Map<String, dynamic>;
listener.onGeoUpdate(
data['type'] as String,
data['updating'] as bool,
data['skipped'] as bool? ?? false,
data['error'] as String?,
);
break;
}
}
});
+3 -3
View File
@@ -38,7 +38,7 @@ mixin CoreInterface {
Future<String>? getExternalProvider(String externalProviderName);
Future<String> updateGeoData(UpdateGeoDataParams params);
Future<String> updateGeoData(String type);
Future<String> sideLoadExternalProvider({
required String providerName,
@@ -214,10 +214,10 @@ abstract class CoreHandlerInterface with CoreInterface {
}
@override
Future<String> updateGeoData(UpdateGeoDataParams params) async {
Future<String> updateGeoData(String type) async {
return await _invoke<String>(
method: ActionMethod.updateGeoData,
data: json.encode(params),
data: type,
) ??
'';
}
+42 -42
View File
@@ -34,50 +34,50 @@ class IPCCoreTransport {
try {
final stream = restartIpcServer(name: address);
_subscription = stream.listen(
(data) {
if (data.isEmpty) return;
final type = data[0];
final payload = data.length > 1 ? data.sublist(1) : Uint8List(0);
switch (type) {
case _typeReady:
commonPrint.log('IPC Ready');
if (_readyCompleter.isCompleted) {
(data) {
if (data.isEmpty) return;
final type = data[0];
final payload = data.length > 1 ? data.sublist(1) : Uint8List(0);
switch (type) {
case _typeReady:
commonPrint.log('IPC Ready');
if (_readyCompleter.isCompleted) {
break;
}
_readyCompleter.complete();
break;
}
_readyCompleter.complete();
break;
case _typeConnected:
commonPrint.log('IPC Connected');
if (_completer.isCompleted) {
case _typeConnected:
commonPrint.log('IPC Connected');
if (_completer.isCompleted) {
break;
}
_completer.complete();
break;
}
_completer.complete();
break;
case _typeDisconnected:
commonPrint.log('IPC Disconnected');
_completer = Completer<void>();
onDisconnect?.call();
break;
case _typeData:
_dataController.add(payload);
break;
case _typeError:
final msg = utf8.decode(payload);
commonPrint.log('IPC error: $msg', logLevel: LogLevel.error);
break;
default:
commonPrint.log(
'IPC unknown frame type: $type',
logLevel: LogLevel.warning,
);
}
},
onError: (error) {
commonPrint.log('IPC error: $error', logLevel: LogLevel.error);
},
cancelOnError: false,
);
await _readyCompleter.future;
case _typeDisconnected:
commonPrint.log('IPC Disconnected');
_completer = Completer<void>();
onDisconnect?.call();
break;
case _typeData:
_dataController.add(payload);
break;
case _typeError:
final msg = utf8.decode(payload);
commonPrint.log('IPC error: $msg', logLevel: LogLevel.error);
break;
default:
commonPrint.log(
'IPC unknown frame type: $type',
logLevel: LogLevel.warning,
);
}
},
onError: (error) {
commonPrint.log('IPC error: $error', logLevel: LogLevel.error);
},
cancelOnError: false,
);
await _readyCompleter.future;
} catch (e) {
commonPrint.log(
'Failed to start IPC server: $e',
+35 -1
View File
@@ -135,7 +135,7 @@ enum ResultType {
error,
}
enum CoreEventType { log, delay, request, loaded, crash }
enum CoreEventType { log, delay, request, loaded, crash, geoUpdate }
enum InvokeMessageType { protect, process }
@@ -331,6 +331,40 @@ enum DashboardWidget {
enum GeodataLoader { standard, memconservative }
enum GeoResource {
@JsonValue('mmdb')
MMDB,
@JsonValue('asn')
ASN,
@JsonValue('geo-ip')
GEOIP,
@JsonValue('geo-site')
GEOSITE;
static GeoResource fromJson(String value) {
return switch (value) {
'mmdb' => GeoResource.MMDB,
'asn' => GeoResource.ASN,
'geo-ip' || 'geoip' => GeoResource.GEOIP,
'geo-site' || 'geosite' => GeoResource.GEOSITE,
_ => throw ArgumentError.value(value, 'value', 'Invalid geo resource'),
};
}
}
extension GeoResourceExt on GeoResource {
String get value {
return switch (this) {
GeoResource.MMDB => 'mmdb',
GeoResource.ASN => 'asn',
GeoResource.GEOIP => 'geo-ip',
GeoResource.GEOSITE => 'geo-site',
};
}
String get updatingKey => 'geo_resource_$name';
}
enum PageLabel {
dashboard,
proxies,
+4 -1
View File
@@ -251,7 +251,7 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
return;
}
final rule = Rule(
id: widget.rule?.id ?? -1,
id: widget.rule?.id ?? snowflake.id,
ruleAction: _ruleAction,
content: _contentController.text,
ruleTarget: _ruleTargetController.text,
@@ -310,6 +310,9 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
const SizedBox(height: 24),
TextFormField(
keyboardType: TextInputType.text,
inputFormatters: TextInputLimits.limit(
TextInputLimits.rule,
),
onFieldSubmitted: (_) {
_handleSubmit();
},
+59 -30
View File
@@ -33,40 +33,52 @@ class MessageLookup extends MessageLookupByLibrary {
static String m4(label) => "${label} cannot be empty";
static String m5(label) => "Current ${label} already exists";
static String m5(count) => "${count} entries";
static String m6(count) =>
static String m6(label) => "Current ${label} already exists";
static String m7(name) => "${name} skipped";
static String m8(name) => "${name} updated";
static String m9(name) => "Updating ${name}...";
static String m10(count) =>
"${Intl.plural(count, one: '1 hour ago', other: '${count} hours ago')}";
static String m7(target) => "${target} is an invalid policy";
static String m11(count) => "${count} hours";
static String m8(proxyName) => "${proxyName} is an invalid proxy";
static String m12(target) => "${target} is an invalid policy";
static String m9(providerName) =>
static String m13(proxyName) => "${proxyName} is an invalid proxy";
static String m14(providerName) =>
"${providerName} is an invalid proxy provider";
static String m10(subRule) => "${subRule} is an invalid SUB_RULE";
static String m15(subRule) => "${subRule} is an invalid SUB_RULE";
static String m11(appName) =>
static String m16(appName) =>
"1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check ${appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.";
static String m12(count) =>
static String m17(count) =>
"${Intl.plural(count, one: '1 minute ago', other: '${count} minutes ago')}";
static String m13(count) =>
static String m18(count) =>
"${Intl.plural(count, one: '1 month ago', other: '${count} months ago')}";
static String m14(label) => "No ${label} yet";
static String m19(label) => "No ${label} yet";
static String m15(label) => "${label} must be a number";
static String m20(label) => "${label} must be a number";
static String m16(label) => "${label} must be between 1024 and 49151";
static String m21(label) => "${label} must be between 1024 and 49151";
static String m17(count) => "${count} items have been selected";
static String m22(count) => "${count} seconds";
static String m18(label) => "${label} must be a url";
static String m23(count) => "${count} items have been selected";
static String m19(count) =>
static String m24(label) => "${label} must be a url";
static String m25(count) =>
"${Intl.plural(count, one: '1 year ago', other: '${count} years ago')}";
final messages = _notInlinedMessages(_notInlinedMessages);
@@ -343,6 +355,7 @@ class MessageLookup extends MessageLookupByLibrary {
"emptyTip": m4,
"en": MessageLookupByLibrary.simpleMessage("English"),
"entries": MessageLookupByLibrary.simpleMessage(" entries"),
"entriesCount": m5,
"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",
@@ -355,7 +368,7 @@ class MessageLookup extends MessageLookupByLibrary {
"When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
),
"excludeType": MessageLookupByLibrary.simpleMessage("Exclude type"),
"existsTip": m5,
"existsTip": m6,
"exit": MessageLookupByLibrary.simpleMessage("Exit"),
"expand": MessageLookupByLibrary.simpleMessage("Standard"),
"expectedStatus": MessageLookupByLibrary.simpleMessage("Expected status"),
@@ -394,6 +407,18 @@ class MessageLookup extends MessageLookupByLibrary {
),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("FruitSalad"),
"general": MessageLookupByLibrary.simpleMessage("General"),
"geoAutoUpdate": MessageLookupByLibrary.simpleMessage("Auto Update"),
"geoAutoUpdateInterval": MessageLookupByLibrary.simpleMessage(
"Auto Update Interval",
),
"geoAutoUpdateIntervalTip": MessageLookupByLibrary.simpleMessage(
"Auto update interval must be greater than 0",
),
"geoOptions": MessageLookupByLibrary.simpleMessage("Geo Options"),
"geoResources": MessageLookupByLibrary.simpleMessage("Geo Resources"),
"geoSkipped": m7,
"geoUpdated": m8,
"geoUpdating": m9,
"geodataLoader": MessageLookupByLibrary.simpleMessage(
"Geo Low Memory Mode",
),
@@ -420,7 +445,9 @@ class MessageLookup extends MessageLookupByLibrary {
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage(
"Use keyboard to control applications",
),
"hoursAgo": m6,
"hours": MessageLookupByLibrary.simpleMessage("hours"),
"hoursAgo": m10,
"hoursCount": m11,
"icon": MessageLookupByLibrary.simpleMessage("Icon"),
"iconRecords": MessageLookupByLibrary.simpleMessage("Icon records"),
"iconStyle": MessageLookupByLibrary.simpleMessage("Icon style"),
@@ -464,10 +491,10 @@ class MessageLookup extends MessageLookupByLibrary {
"invalidBackupFile": MessageLookupByLibrary.simpleMessage(
"Invalid backup file",
),
"invalidPolicy": m7,
"invalidProxy": m8,
"invalidProxyProvider": m9,
"invalidSubRule": m10,
"invalidPolicy": m12,
"invalidProxy": m13,
"invalidProxyProvider": m14,
"invalidSubRule": m15,
"ipcidr": MessageLookupByLibrary.simpleMessage("Ipcidr"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
"When turned on it will be able to receive IPv6 traffic",
@@ -501,7 +528,7 @@ class MessageLookup extends MessageLookupByLibrary {
"locationPermissionDesc": MessageLookupByLibrary.simpleMessage(
"According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
),
"locationPermissionGuide": m11,
"locationPermissionGuide": m16,
"locationPermissionRequired": MessageLookupByLibrary.simpleMessage(
"Location Permission Required",
),
@@ -531,11 +558,11 @@ class MessageLookup extends MessageLookupByLibrary {
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
"Modify the default system exit event",
),
"minutesAgo": m12,
"minutesAgo": m17,
"mixedPort": MessageLookupByLibrary.simpleMessage("Mixed Port"),
"mode": MessageLookupByLibrary.simpleMessage("Mode"),
"monochromeScheme": MessageLookupByLibrary.simpleMessage("Monochrome"),
"monthsAgo": m13,
"monthsAgo": m18,
"more": MessageLookupByLibrary.simpleMessage("More"),
"name": MessageLookupByLibrary.simpleMessage("Name"),
"nameserver": MessageLookupByLibrary.simpleMessage("Nameserver"),
@@ -581,8 +608,8 @@ class MessageLookup extends MessageLookupByLibrary {
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"No profile, Please add a profile",
),
"nullTip": m14,
"numberTip": m15,
"nullTip": m19,
"numberTip": m20,
"onDemand": MessageLookupByLibrary.simpleMessage("On Demand"),
"onDemandDesc": MessageLookupByLibrary.simpleMessage(
"Configure the program running state for specific scenarios",
@@ -631,7 +658,7 @@ class MessageLookup extends MessageLookupByLibrary {
"portConflictTip": MessageLookupByLibrary.simpleMessage(
"Please enter a different port",
),
"portTip": m16,
"portTip": m21,
"preferH3Desc": MessageLookupByLibrary.simpleMessage(
"Prioritize the use of DOH\'s http/3",
),
@@ -888,6 +915,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"search": MessageLookupByLibrary.simpleMessage("Search"),
"seconds": MessageLookupByLibrary.simpleMessage("Seconds"),
"secondsCount": m22,
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
"selectProxies": MessageLookupByLibrary.simpleMessage("Select proxies"),
"selectProxyProviders": MessageLookupByLibrary.simpleMessage(
@@ -903,7 +931,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Please select sub rule",
),
"selected": MessageLookupByLibrary.simpleMessage("Selected"),
"selectedCountTitle": m17,
"selectedCountTitle": m23,
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
"show": MessageLookupByLibrary.simpleMessage("Show"),
"shrink": MessageLookupByLibrary.simpleMessage("Shrink"),
@@ -1003,9 +1031,10 @@ class MessageLookup extends MessageLookupByLibrary {
"urlDesc": MessageLookupByLibrary.simpleMessage(
"Obtain profile through URL",
),
"urlTip": m18,
"urlTip": m24,
"useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("Use system hosts"),
"userAgent": MessageLookupByLibrary.simpleMessage("User-Agent"),
"value": MessageLookupByLibrary.simpleMessage("Value"),
"vibrantScheme": MessageLookupByLibrary.simpleMessage("Vibrant"),
"view": MessageLookupByLibrary.simpleMessage("View"),
@@ -1022,7 +1051,7 @@ class MessageLookup extends MessageLookupByLibrary {
"WebDAV configuration",
),
"whitelistMode": MessageLookupByLibrary.simpleMessage("Whitelist mode"),
"yearsAgo": m19,
"yearsAgo": m25,
"zh_CN": MessageLookupByLibrary.simpleMessage("Simplified Chinese"),
};
}
+57 -30
View File
@@ -30,36 +30,48 @@ class MessageLookup extends MessageLookupByLibrary {
static String m4(label) => "${label}は空欄にできません";
static String m5(label) => "現在の${label}は既に存在しています";
static String m5(count) => "${count} エントリ";
static String m6(count) => "${count}時間前";
static String m6(label) => "現在の${label}は既に存在しています";
static String m7(target) => "${target} は無効なポリシーです";
static String m7(name) => "${name} スキップ済み";
static String m8(proxyName) => "${proxyName} は無効なプロキシです";
static String m8(name) => "${name} 更新済み";
static String m9(providerName) => "${providerName} は無効なプロキシプロバイダーです";
static String m9(name) => "${name}を更新中...";
static String m10(subRule) => "${subRule} は無効なSUB_RULEです";
static String m10(count) => "${count}時間前";
static String m11(appName) =>
static String m11(count) => "${count} 時間";
static String m12(target) => "${target} は無効なポリシーです";
static String m13(proxyName) => "${proxyName} は無効なプロキシです";
static String m14(providerName) => "${providerName} は無効なプロキシプロバイダーです";
static String m15(subRule) => "${subRule} は無効なSUB_RULEです";
static String m16(appName) =>
"1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check ${appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.";
static String m12(count) => "${count}分前";
static String m17(count) => "${count}分前";
static String m13(count) => "${count}ヶ月前";
static String m18(count) => "${count}ヶ月前";
static String m14(label) => "まだ${label}はありません";
static String m19(label) => "まだ${label}はありません";
static String m15(label) => "${label}は数字でなければなりません";
static String m20(label) => "${label}は数字でなければなりません";
static String m16(label) => "${label} は 1024 から 49151 の間でなければなりません";
static String m21(label) => "${label} は 1024 から 49151 の間でなければなりません";
static String m17(count) => "${count} 項目が選択されています";
static String m22(count) => "${count} 秒";
static String m18(label) => "${label}はURLである必要があります";
static String m23(count) => "${count} 項目が選択されています";
static String m19(count) => "${count}年前";
static String m24(label) => "${label}はURLである必要があります";
static String m25(count) => "${count}年前";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
@@ -265,6 +277,7 @@ class MessageLookup extends MessageLookupByLibrary {
"emptyTip": m4,
"en": MessageLookupByLibrary.simpleMessage("英語"),
"entries": MessageLookupByLibrary.simpleMessage(" エントリ"),
"entriesCount": m5,
"exclude": MessageLookupByLibrary.simpleMessage("最近のタスクから非表示"),
"excludeDesc": MessageLookupByLibrary.simpleMessage(
"アプリがバックグラウンド時に最近のタスクから非表示",
@@ -275,7 +288,7 @@ class MessageLookup extends MessageLookupByLibrary {
"When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
),
"excludeType": MessageLookupByLibrary.simpleMessage("除外タイプ"),
"existsTip": m5,
"existsTip": m6,
"exit": MessageLookupByLibrary.simpleMessage("終了"),
"expand": MessageLookupByLibrary.simpleMessage("標準"),
"expectedStatus": MessageLookupByLibrary.simpleMessage("期待されるステータス"),
@@ -310,6 +323,16 @@ class MessageLookup extends MessageLookupByLibrary {
),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("フルーツサラダ"),
"general": MessageLookupByLibrary.simpleMessage("一般"),
"geoAutoUpdate": MessageLookupByLibrary.simpleMessage("自動更新"),
"geoAutoUpdateInterval": MessageLookupByLibrary.simpleMessage("自動更新間隔"),
"geoAutoUpdateIntervalTip": MessageLookupByLibrary.simpleMessage(
"自動更新間隔は0より大きくなければなりません",
),
"geoOptions": MessageLookupByLibrary.simpleMessage("Geoオプション"),
"geoResources": MessageLookupByLibrary.simpleMessage("Geoリソース"),
"geoSkipped": m7,
"geoUpdated": m8,
"geoUpdating": m9,
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低メモリモード"),
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとGeo低メモリローダーを使用",
@@ -328,7 +351,9 @@ class MessageLookup extends MessageLookupByLibrary {
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage(
"キーボードでアプリを制御",
),
"hoursAgo": m6,
"hours": MessageLookupByLibrary.simpleMessage("時間"),
"hoursAgo": m10,
"hoursCount": m11,
"icon": MessageLookupByLibrary.simpleMessage("アイコン"),
"iconRecords": MessageLookupByLibrary.simpleMessage("アイコン履歴"),
"iconStyle": MessageLookupByLibrary.simpleMessage("アイコンスタイル"),
@@ -360,10 +385,10 @@ class MessageLookup extends MessageLookupByLibrary {
"interval": MessageLookupByLibrary.simpleMessage("インターバル"),
"intranetIP": MessageLookupByLibrary.simpleMessage("イントラネットIP"),
"invalidBackupFile": MessageLookupByLibrary.simpleMessage("無効なバックアップファイル"),
"invalidPolicy": m7,
"invalidProxy": m8,
"invalidProxyProvider": m9,
"invalidSubRule": m10,
"invalidPolicy": m12,
"invalidProxy": m13,
"invalidProxyProvider": m14,
"invalidSubRule": m15,
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("有効化するとIPv6トラフィックを受信可能"),
"ipv6InboundDesc": MessageLookupByLibrary.simpleMessage("IPv6インバウンドを許可"),
@@ -391,7 +416,7 @@ class MessageLookup extends MessageLookupByLibrary {
"locationPermissionDesc": MessageLookupByLibrary.simpleMessage(
"According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
),
"locationPermissionGuide": m11,
"locationPermissionGuide": m16,
"locationPermissionRequired": MessageLookupByLibrary.simpleMessage(
"Location Permission Required",
),
@@ -415,11 +440,11 @@ class MessageLookup extends MessageLookupByLibrary {
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
"システムの終了イベントを変更",
),
"minutesAgo": m12,
"minutesAgo": m17,
"mixedPort": MessageLookupByLibrary.simpleMessage("混合ポート"),
"mode": MessageLookupByLibrary.simpleMessage("モード"),
"monochromeScheme": MessageLookupByLibrary.simpleMessage("モノクローム"),
"monthsAgo": m13,
"monthsAgo": m18,
"more": MessageLookupByLibrary.simpleMessage("詳細"),
"name": MessageLookupByLibrary.simpleMessage("名前"),
"nameserver": MessageLookupByLibrary.simpleMessage("ネームサーバー"),
@@ -453,8 +478,8 @@ class MessageLookup extends MessageLookupByLibrary {
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"プロファイルがありません。追加してください",
),
"nullTip": m14,
"numberTip": m15,
"nullTip": m19,
"numberTip": m20,
"onDemand": MessageLookupByLibrary.simpleMessage("On Demand"),
"onDemandDesc": MessageLookupByLibrary.simpleMessage(
"Configure the program running state for specific scenarios",
@@ -497,7 +522,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"port": MessageLookupByLibrary.simpleMessage("ポート"),
"portConflictTip": MessageLookupByLibrary.simpleMessage("別のポートを入力してください"),
"portTip": m16,
"portTip": m21,
"preferH3Desc": MessageLookupByLibrary.simpleMessage("DOHのHTTP/3を優先使用"),
"prerequisites": MessageLookupByLibrary.simpleMessage("Prerequisites"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage("キーボードを押してください"),
@@ -716,6 +741,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"search": MessageLookupByLibrary.simpleMessage("検索"),
"seconds": MessageLookupByLibrary.simpleMessage("秒"),
"secondsCount": m22,
"selectAll": MessageLookupByLibrary.simpleMessage("すべて選択"),
"selectProxies": MessageLookupByLibrary.simpleMessage("プロキシを選択"),
"selectProxyProviders": MessageLookupByLibrary.simpleMessage(
@@ -727,7 +753,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"selectSubRule": MessageLookupByLibrary.simpleMessage("サブルールを選択してください"),
"selected": MessageLookupByLibrary.simpleMessage("選択済み"),
"selectedCountTitle": m17,
"selectedCountTitle": m23,
"settings": MessageLookupByLibrary.simpleMessage("設定"),
"show": MessageLookupByLibrary.simpleMessage("表示"),
"shrink": MessageLookupByLibrary.simpleMessage("縮小"),
@@ -809,9 +835,10 @@ class MessageLookup extends MessageLookupByLibrary {
"upload": MessageLookupByLibrary.simpleMessage("アップロード"),
"url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage("URL経由でプロファイルを取得"),
"urlTip": m18,
"urlTip": m24,
"useHosts": MessageLookupByLibrary.simpleMessage("ホストを使用"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("システムホストを使用"),
"userAgent": MessageLookupByLibrary.simpleMessage("ユーザーエージェント"),
"value": MessageLookupByLibrary.simpleMessage("値"),
"vibrantScheme": MessageLookupByLibrary.simpleMessage("ビブラント"),
"view": MessageLookupByLibrary.simpleMessage("表示"),
@@ -824,7 +851,7 @@ class MessageLookup extends MessageLookupByLibrary {
"vpnTip": MessageLookupByLibrary.simpleMessage("変更はVPN再起動後に有効"),
"webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV設定"),
"whitelistMode": MessageLookupByLibrary.simpleMessage("ホワイトリストモード"),
"yearsAgo": m19,
"yearsAgo": m25,
"zh_CN": MessageLookupByLibrary.simpleMessage("簡体字中国語"),
};
}
+59 -30
View File
@@ -32,40 +32,52 @@ class MessageLookup extends MessageLookupByLibrary {
static String m4(label) => "${label} не может быть пустым";
static String m5(label) => "Текущий ${label} уже существует";
static String m5(count) => "${count} записей";
static String m6(count) =>
static String m6(label) => "Текущий ${label} уже существует";
static String m7(name) => "${name} пропущено";
static String m8(name) => "${name} обновлено";
static String m9(name) => "Обновление ${name}...";
static String m10(count) =>
"${Intl.plural(count, one: '${count} час назад', few: '${count} часа назад', many: '${count} часов назад', other: '${count} часа назад')}";
static String m7(target) => "${target} является недопустимой политикой";
static String m11(count) => "${count} часов";
static String m8(proxyName) => "${proxyName} является недопустимым прокси";
static String m12(target) => "${target} является недопустимой политикой";
static String m9(providerName) =>
static String m13(proxyName) => "${proxyName} является недопустимым прокси";
static String m14(providerName) =>
"${providerName} является недопустимым провайдером прокси";
static String m10(subRule) => "${subRule} является недопустимым подправилом";
static String m15(subRule) => "${subRule} является недопустимым подправилом";
static String m11(appName) =>
static String m16(appName) =>
"1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check ${appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.";
static String m12(count) =>
static String m17(count) =>
"${Intl.plural(count, one: '${count} минута назад', few: '${count} минуты назад', many: '${count} минут назад', other: '${count} минуты назад')}";
static String m13(count) =>
static String m18(count) =>
"${Intl.plural(count, one: '${count} месяц назад', few: '${count} месяца назад', many: '${count} месяцев назад', other: '${count} месяца назад')}";
static String m14(label) => "${label} пока отсутствуют";
static String m19(label) => "${label} пока отсутствуют";
static String m15(label) => "${label} должно быть числом";
static String m20(label) => "${label} должно быть числом";
static String m16(label) => "${label} должен быть числом от 1024 до 49151";
static String m21(label) => "${label} должен быть числом от 1024 до 49151";
static String m17(count) => "Выбрано ${count} элементов";
static String m22(count) => "${count} секунд";
static String m18(label) => "${label} должен быть URL";
static String m23(count) => "Выбрано ${count} элементов";
static String m19(count) =>
static String m24(label) => "${label} должен быть URL";
static String m25(count) =>
"${Intl.plural(count, one: '${count} год назад', few: '${count} года назад', many: '${count} лет назад', other: '${count} года назад')}";
final messages = _notInlinedMessages(_notInlinedMessages);
@@ -352,6 +364,7 @@ class MessageLookup extends MessageLookupByLibrary {
"emptyTip": m4,
"en": MessageLookupByLibrary.simpleMessage("Английский"),
"entries": MessageLookupByLibrary.simpleMessage(" записей"),
"entriesCount": m5,
"exclude": MessageLookupByLibrary.simpleMessage(
"Скрыть из последних задач",
),
@@ -366,7 +379,7 @@ class MessageLookup extends MessageLookupByLibrary {
"When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
),
"excludeType": MessageLookupByLibrary.simpleMessage("Тип исключения"),
"existsTip": m5,
"existsTip": m6,
"exit": MessageLookupByLibrary.simpleMessage("Выход"),
"expand": MessageLookupByLibrary.simpleMessage("Стандартный"),
"expectedStatus": MessageLookupByLibrary.simpleMessage("Ожидаемый статус"),
@@ -409,6 +422,18 @@ class MessageLookup extends MessageLookupByLibrary {
),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("Фруктовый микс"),
"general": MessageLookupByLibrary.simpleMessage("Общие"),
"geoAutoUpdate": MessageLookupByLibrary.simpleMessage("Автообновление"),
"geoAutoUpdateInterval": MessageLookupByLibrary.simpleMessage(
"Интервал автообновления",
),
"geoAutoUpdateIntervalTip": MessageLookupByLibrary.simpleMessage(
"Интервал автообновления должен быть больше 0",
),
"geoOptions": MessageLookupByLibrary.simpleMessage("Настройки Geo"),
"geoResources": MessageLookupByLibrary.simpleMessage("Ресурсы Geo"),
"geoSkipped": m7,
"geoUpdated": m8,
"geoUpdating": m9,
"geodataLoader": MessageLookupByLibrary.simpleMessage(
"Режим низкого потребления памяти для геоданных",
),
@@ -437,7 +462,9 @@ class MessageLookup extends MessageLookupByLibrary {
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage(
"Использование клавиатуры для управления приложением",
),
"hoursAgo": m6,
"hours": MessageLookupByLibrary.simpleMessage("часов"),
"hoursAgo": m10,
"hoursCount": m11,
"icon": MessageLookupByLibrary.simpleMessage("Иконка"),
"iconRecords": MessageLookupByLibrary.simpleMessage("История иконок"),
"iconStyle": MessageLookupByLibrary.simpleMessage("Стиль иконки"),
@@ -483,10 +510,10 @@ class MessageLookup extends MessageLookupByLibrary {
"invalidBackupFile": MessageLookupByLibrary.simpleMessage(
"Неверный файл резервной копии",
),
"invalidPolicy": m7,
"invalidProxy": m8,
"invalidProxyProvider": m9,
"invalidSubRule": m10,
"invalidPolicy": m12,
"invalidProxy": m13,
"invalidProxyProvider": m14,
"invalidSubRule": m15,
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
"При включении будет возможно получать IPv6 трафик",
@@ -520,7 +547,7 @@ class MessageLookup extends MessageLookupByLibrary {
"locationPermissionDesc": MessageLookupByLibrary.simpleMessage(
"According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
),
"locationPermissionGuide": m11,
"locationPermissionGuide": m16,
"locationPermissionRequired": MessageLookupByLibrary.simpleMessage(
"Location Permission Required",
),
@@ -558,11 +585,11 @@ class MessageLookup extends MessageLookupByLibrary {
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
"Изменить стандартное событие выхода из системы",
),
"minutesAgo": m12,
"minutesAgo": m17,
"mixedPort": MessageLookupByLibrary.simpleMessage("Смешанный порт"),
"mode": MessageLookupByLibrary.simpleMessage("Режим"),
"monochromeScheme": MessageLookupByLibrary.simpleMessage("Монохром"),
"monthsAgo": m13,
"monthsAgo": m18,
"more": MessageLookupByLibrary.simpleMessage("Еще"),
"name": MessageLookupByLibrary.simpleMessage("Имя"),
"nameserver": MessageLookupByLibrary.simpleMessage("Сервер имен"),
@@ -608,8 +635,8 @@ class MessageLookup extends MessageLookupByLibrary {
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"Нет профиля, пожалуйста, добавьте профиль",
),
"nullTip": m14,
"numberTip": m15,
"nullTip": m19,
"numberTip": m20,
"onDemand": MessageLookupByLibrary.simpleMessage("On Demand"),
"onDemandDesc": MessageLookupByLibrary.simpleMessage(
"Configure the program running state for specific scenarios",
@@ -666,7 +693,7 @@ class MessageLookup extends MessageLookupByLibrary {
"portConflictTip": MessageLookupByLibrary.simpleMessage(
"Введите другой порт",
),
"portTip": m16,
"portTip": m21,
"preferH3Desc": MessageLookupByLibrary.simpleMessage(
"Приоритетное использование HTTP/3 для DOH",
),
@@ -935,6 +962,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"search": MessageLookupByLibrary.simpleMessage("Поиск"),
"seconds": MessageLookupByLibrary.simpleMessage("Секунд"),
"secondsCount": m22,
"selectAll": MessageLookupByLibrary.simpleMessage("Выбрать все"),
"selectProxies": MessageLookupByLibrary.simpleMessage("Выбрать прокси"),
"selectProxyProviders": MessageLookupByLibrary.simpleMessage(
@@ -950,7 +978,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Пожалуйста, выберите подправило",
),
"selected": MessageLookupByLibrary.simpleMessage("Выбрано"),
"selectedCountTitle": m17,
"selectedCountTitle": m23,
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"show": MessageLookupByLibrary.simpleMessage("Показать"),
"shrink": MessageLookupByLibrary.simpleMessage("Сжать"),
@@ -1064,11 +1092,12 @@ class MessageLookup extends MessageLookupByLibrary {
"urlDesc": MessageLookupByLibrary.simpleMessage(
"Получить профиль через URL",
),
"urlTip": m18,
"urlTip": m24,
"useHosts": MessageLookupByLibrary.simpleMessage("Использовать hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage(
"Использовать системные hosts",
),
"userAgent": MessageLookupByLibrary.simpleMessage("User-Agent"),
"value": MessageLookupByLibrary.simpleMessage("Значение"),
"vibrantScheme": MessageLookupByLibrary.simpleMessage("Яркие"),
"view": MessageLookupByLibrary.simpleMessage("Просмотр"),
@@ -1087,7 +1116,7 @@ class MessageLookup extends MessageLookupByLibrary {
"whitelistMode": MessageLookupByLibrary.simpleMessage(
"Режим белого списка",
),
"yearsAgo": m19,
"yearsAgo": m25,
"zh_CN": MessageLookupByLibrary.simpleMessage("Упрощенный китайский"),
};
}
+57 -30
View File
@@ -30,36 +30,48 @@ class MessageLookup extends MessageLookupByLibrary {
static String m4(label) => "${label}不能为空";
static String m5(label) => "${label}当前已存在";
static String m5(count) => "${count} 个条目";
static String m6(count) => "${count} 小时前";
static String m6(label) => "${label}当前已存在";
static String m7(target) => "${target} 是一个无效的策略";
static String m7(name) => "${name} 已跳过";
static String m8(proxyName) => "${proxyName} 是一个无效的代理";
static String m8(name) => "${name} 已更新";
static String m9(providerName) => "${providerName} 是一个无效的代理集";
static String m9(name) => "正在更新 ${name}...";
static String m10(subRule) => "${subRule} 是一个无效的SUB_RULE";
static String m10(count) => "${count} 小时前";
static String m11(appName) =>
static String m11(count) => "${count} 小时";
static String m12(target) => "${target} 是一个无效的策略";
static String m13(proxyName) => "${proxyName} 是一个无效的代理";
static String m14(providerName) => "${providerName} 是一个无效的代理集";
static String m15(subRule) => "${subRule} 是一个无效的SUB_RULE";
static String m16(appName) =>
"1. 打开 系统设置 > 隐私与安全性\n2. 选择 定位服务\n3. 在右侧列表中找到并勾选 ${appName}\n\n完成设置后,返回应用即可正常使用。感谢您的配合。";
static String m12(count) => "${count} 分钟前";
static String m17(count) => "${count} 分钟前";
static String m13(count) => "${count} 个月前";
static String m18(count) => "${count} 个月前";
static String m14(label) => "暂无${label}";
static String m19(label) => "暂无${label}";
static String m15(label) => "${label}必须为数字";
static String m20(label) => "${label}必须为数字";
static String m16(label) => "${label} 必须在 1024 到 49151 之间";
static String m21(label) => "${label} 必须在 1024 到 49151 之间";
static String m17(count) => "已选择 ${count} 项";
static String m22(count) => "${count} 秒";
static String m18(label) => "${label}必须为URL";
static String m23(count) => "已选择 ${count} 项";
static String m19(count) => "${count} 年前";
static String m24(label) => "${label}必须为URL";
static String m25(count) => "${count} 年前";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
@@ -237,6 +249,7 @@ class MessageLookup extends MessageLookupByLibrary {
"emptyTip": m4,
"en": MessageLookupByLibrary.simpleMessage("英语"),
"entries": MessageLookupByLibrary.simpleMessage("个条目"),
"entriesCount": m5,
"exclude": MessageLookupByLibrary.simpleMessage("从最近任务中隐藏"),
"excludeDesc": MessageLookupByLibrary.simpleMessage("应用在后台时,从最近任务中隐藏应用"),
"excludeProxyFilter": MessageLookupByLibrary.simpleMessage("排除节点过滤器"),
@@ -245,7 +258,7 @@ class MessageLookup extends MessageLookupByLibrary {
"连接到被排除SSID的WIFI时,将会自动切换应用运行状态",
),
"excludeType": MessageLookupByLibrary.simpleMessage("排除类型"),
"existsTip": m5,
"existsTip": m6,
"exit": MessageLookupByLibrary.simpleMessage("退出"),
"expand": MessageLookupByLibrary.simpleMessage("标准"),
"expectedStatus": MessageLookupByLibrary.simpleMessage("预期状态"),
@@ -274,6 +287,16 @@ class MessageLookup extends MessageLookupByLibrary {
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要强制重启核心吗?"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("果缤纷"),
"general": MessageLookupByLibrary.simpleMessage("常规"),
"geoAutoUpdate": MessageLookupByLibrary.simpleMessage("自动更新"),
"geoAutoUpdateInterval": MessageLookupByLibrary.simpleMessage("自动更新间隔"),
"geoAutoUpdateIntervalTip": MessageLookupByLibrary.simpleMessage(
"自动更新间隔必须大于0",
),
"geoOptions": MessageLookupByLibrary.simpleMessage("Geo 选项"),
"geoResources": MessageLookupByLibrary.simpleMessage("Geo 资源"),
"geoSkipped": m7,
"geoUpdated": m8,
"geoUpdating": m9,
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"),
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"),
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip代码"),
@@ -288,7 +311,9 @@ class MessageLookup extends MessageLookupByLibrary {
"hotkeyConflict": MessageLookupByLibrary.simpleMessage("快捷键冲突"),
"hotkeyManagement": MessageLookupByLibrary.simpleMessage("快捷键管理"),
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage("使用键盘控制应用程序"),
"hoursAgo": m6,
"hours": MessageLookupByLibrary.simpleMessage("小时"),
"hoursAgo": m10,
"hoursCount": m11,
"icon": MessageLookupByLibrary.simpleMessage("图片"),
"iconRecords": MessageLookupByLibrary.simpleMessage("图标记录"),
"iconStyle": MessageLookupByLibrary.simpleMessage("图标样式"),
@@ -316,10 +341,10 @@ class MessageLookup extends MessageLookupByLibrary {
"interval": MessageLookupByLibrary.simpleMessage("间隔"),
"intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"),
"invalidBackupFile": MessageLookupByLibrary.simpleMessage("无效备份文件"),
"invalidPolicy": m7,
"invalidProxy": m8,
"invalidProxyProvider": m9,
"invalidSubRule": m10,
"invalidPolicy": m12,
"invalidProxy": m13,
"invalidProxyProvider": m14,
"invalidSubRule": m15,
"ipcidr": MessageLookupByLibrary.simpleMessage("IP/掩码"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"),
"ipv6InboundDesc": MessageLookupByLibrary.simpleMessage("允许IPv6入站"),
@@ -343,7 +368,7 @@ class MessageLookup extends MessageLookupByLibrary {
"locationPermissionDesc": MessageLookupByLibrary.simpleMessage(
"根据系统要求,获取Wi-Fi名称需要您授予位置权限。",
),
"locationPermissionGuide": m11,
"locationPermissionGuide": m16,
"locationPermissionRequired": MessageLookupByLibrary.simpleMessage(
"需要位置权限",
),
@@ -365,11 +390,11 @@ class MessageLookup extends MessageLookupByLibrary {
"min": MessageLookupByLibrary.simpleMessage("最小"),
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"),
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage("修改系统默认退出事件"),
"minutesAgo": m12,
"minutesAgo": m17,
"mixedPort": MessageLookupByLibrary.simpleMessage("混合端口"),
"mode": MessageLookupByLibrary.simpleMessage("模式"),
"monochromeScheme": MessageLookupByLibrary.simpleMessage("单色"),
"monthsAgo": m13,
"monthsAgo": m18,
"more": MessageLookupByLibrary.simpleMessage("更多"),
"name": MessageLookupByLibrary.simpleMessage("名称"),
"nameserver": MessageLookupByLibrary.simpleMessage("域名服务器"),
@@ -395,8 +420,8 @@ class MessageLookup extends MessageLookupByLibrary {
"none": MessageLookupByLibrary.simpleMessage("无"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"),
"nullProfileDesc": MessageLookupByLibrary.simpleMessage("没有配置文件,请先添加配置文件"),
"nullTip": m14,
"numberTip": m15,
"nullTip": m19,
"numberTip": m20,
"onDemand": MessageLookupByLibrary.simpleMessage("按需运行"),
"onDemandDesc": MessageLookupByLibrary.simpleMessage("配置程序特定场景运行状态"),
"onlyIcon": MessageLookupByLibrary.simpleMessage("仅图标"),
@@ -431,7 +456,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"port": MessageLookupByLibrary.simpleMessage("端口"),
"portConflictTip": MessageLookupByLibrary.simpleMessage("请输入不同的端口"),
"portTip": m16,
"portTip": m21,
"preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"),
"prerequisites": MessageLookupByLibrary.simpleMessage("前置条件"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage("请按下按键"),
@@ -614,6 +639,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"search": MessageLookupByLibrary.simpleMessage("搜索"),
"seconds": MessageLookupByLibrary.simpleMessage("秒"),
"secondsCount": m22,
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
"selectProxies": MessageLookupByLibrary.simpleMessage("选择代理"),
"selectProxyProviders": MessageLookupByLibrary.simpleMessage("选择代理集"),
@@ -621,7 +647,7 @@ class MessageLookup extends MessageLookupByLibrary {
"selectSplitStrategy": MessageLookupByLibrary.simpleMessage("请选择分流策略"),
"selectSubRule": MessageLookupByLibrary.simpleMessage("请选择子规则"),
"selected": MessageLookupByLibrary.simpleMessage("已选择"),
"selectedCountTitle": m17,
"selectedCountTitle": m23,
"settings": MessageLookupByLibrary.simpleMessage("设置"),
"show": MessageLookupByLibrary.simpleMessage("显示"),
"shrink": MessageLookupByLibrary.simpleMessage("紧凑"),
@@ -697,9 +723,10 @@ class MessageLookup extends MessageLookupByLibrary {
"upload": MessageLookupByLibrary.simpleMessage("上传"),
"url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage("通过URL获取配置文件"),
"urlTip": m18,
"urlTip": m24,
"useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"),
"userAgent": MessageLookupByLibrary.simpleMessage("用户代理"),
"value": MessageLookupByLibrary.simpleMessage("值"),
"vibrantScheme": MessageLookupByLibrary.simpleMessage("活力"),
"view": MessageLookupByLibrary.simpleMessage("查看"),
@@ -712,7 +739,7 @@ class MessageLookup extends MessageLookupByLibrary {
"vpnTip": MessageLookupByLibrary.simpleMessage("重启VPN后改变生效"),
"webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV配置"),
"whitelistMode": MessageLookupByLibrary.simpleMessage("白名单模式"),
"yearsAgo": m19,
"yearsAgo": m25,
"zh_CN": MessageLookupByLibrary.simpleMessage("中文简体"),
};
}
+115
View File
@@ -1039,6 +1039,11 @@ class AppLocalizations {
);
}
/// `User-Agent`
String get userAgent {
return Intl.message('User-Agent', name: 'userAgent', desc: '', args: []);
}
/// `Unified delay`
String get unifiedDelay {
return Intl.message(
@@ -4378,6 +4383,116 @@ class AppLocalizations {
String get suspended {
return Intl.message('Suspended...', name: 'suspended', desc: '', args: []);
}
/// `Geo Options`
String get geoOptions {
return Intl.message('Geo Options', name: 'geoOptions', desc: '', args: []);
}
/// `Auto Update`
String get geoAutoUpdate {
return Intl.message(
'Auto Update',
name: 'geoAutoUpdate',
desc: '',
args: [],
);
}
/// `Auto Update Interval`
String get geoAutoUpdateInterval {
return Intl.message(
'Auto Update Interval',
name: 'geoAutoUpdateInterval',
desc: '',
args: [],
);
}
/// `Auto update interval must be greater than 0`
String get geoAutoUpdateIntervalTip {
return Intl.message(
'Auto update interval must be greater than 0',
name: 'geoAutoUpdateIntervalTip',
desc: '',
args: [],
);
}
/// `hours`
String get hours {
return Intl.message('hours', name: 'hours', desc: '', args: []);
}
/// `{count} hours`
String hoursCount(Object count) {
return Intl.message(
'$count hours',
name: 'hoursCount',
desc: '',
args: [count],
);
}
/// `Geo Resources`
String get geoResources {
return Intl.message(
'Geo Resources',
name: 'geoResources',
desc: '',
args: [],
);
}
/// `Updating {name}...`
String geoUpdating(Object name) {
return Intl.message(
'Updating $name...',
name: 'geoUpdating',
desc: '',
args: [name],
);
}
/// `{name} skipped`
String geoSkipped(Object name) {
return Intl.message(
'$name skipped',
name: 'geoSkipped',
desc: '',
args: [name],
);
}
/// `{name} updated`
String geoUpdated(Object name) {
return Intl.message(
'$name updated',
name: 'geoUpdated',
desc: '',
args: [name],
);
}
/// `{count} seconds`
String secondsCount(Object count) {
return Intl.message(
'$count seconds',
name: 'secondsCount',
desc: '',
args: [count],
);
}
/// `{count} entries`
String entriesCount(Object count) {
return Intl.message(
'$count entries',
name: 'entriesCount',
desc: '',
args: [count],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
+2 -2
View File
@@ -11,8 +11,8 @@ import 'application.dart';
import 'common/common.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
try {
WidgetsFlutterBinding.ensureInitialized();
if (system.isDesktop) {
await RustLib.init();
}
@@ -26,7 +26,7 @@ Future<void> main() async {
),
);
} catch (e, s) {
return runApp(
runApp(
MaterialApp(
home: InitErrorScreen(error: e, stack: s),
),
+22 -1
View File
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/enum/enum.dart';
@@ -72,7 +74,7 @@ class _CoreContainerState extends ConsumerState<CoreManager>
@override
void onLog(Log log) {
// ref.read(logsProvider.notifier).add(log);
ref.read(logsProvider.notifier).add(log);
if (log.logLevel == LogLevel.error) {
globalState.showNotifier(log.payload);
}
@@ -109,4 +111,23 @@ class _CoreContainerState extends ConsumerState<CoreManager>
await coreController.shutdown(false);
super.onCrash(message);
}
@override
void onGeoUpdate(String geoType, bool updating, bool skipped, String? error) {
final geoResource = GeoResource.fromJson(geoType.toLowerCase());
final key = geoResource.updatingKey;
final l10n = currentAppLocalizations;
if (updating) {
globalState.showNotifier(l10n.geoUpdating(geoResource.name));
} else if (skipped) {
globalState.showNotifier(l10n.geoSkipped(geoResource.name));
} else {
globalState.showNotifier(l10n.geoUpdated(geoResource.name));
}
ref.read(isUpdatingProvider(key).notifier).value = updating;
if (!updating && error != null && error.isNotEmpty) {
globalState.showNotifier(error);
}
super.onGeoUpdate(geoType, updating, skipped, error);
}
}
+3 -3
View File
@@ -73,7 +73,7 @@ class _HotKeyManagerState extends ConsumerState<HotKeyManager> {
await Future.wait(hotkeyActionHandles);
}
Shortcuts _buildShortcuts(Widget child) {
Shortcuts _buildCloseShortcuts(Widget child) {
return Shortcuts(
shortcuts: {
utils.controlSingleActivator(LogicalKeyboardKey.keyW):
@@ -84,7 +84,7 @@ class _HotKeyManagerState extends ConsumerState<HotKeyManager> {
CloseWindowIntent: CallbackAction<CloseWindowIntent>(
onInvoke: (_) => globalState.container
.read(systemActionProvider.notifier)
.handleBackOrExit(),
.handleClose(false),
),
DoNothingIntent: CallbackAction<DoNothingIntent>(
onInvoke: (_) => null,
@@ -97,6 +97,6 @@ class _HotKeyManagerState extends ConsumerState<HotKeyManager> {
@override
Widget build(BuildContext context) {
return _buildShortcuts(widget.child);
return _buildCloseShortcuts(widget.child);
}
}
+19 -36
View File
@@ -3,7 +3,6 @@ import 'dart:math';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/common/theme.dart';
import 'package:fl_clash/providers/action.dart';
import 'package:fl_clash/providers/app.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
@@ -21,41 +20,25 @@ class ThemeManager extends ConsumerWidget {
if (!system.isAndroid) {
return child;
}
return AnnotatedRegion<SystemUiMode>(
sized: false,
value: SystemUiMode.edgeToEdge,
child: Consumer(
builder: (context, ref, _) {
final brightness = ref.watch(currentBrightnessProvider);
final iconBrightness = brightness == Brightness.light
? Brightness.dark
: Brightness.light;
WidgetsBinding.instance.addPostFrameCallback((_) {
ref
.read(systemUiOverlayStyleStateProvider.notifier)
.update(
(state) => state.copyWith(
statusBarColor: Colors.transparent,
statusBarIconBrightness: iconBrightness,
systemNavigationBarIconBrightness: iconBrightness,
systemNavigationBarColor: context.colorScheme.surface,
systemNavigationBarDividerColor: Colors.transparent,
),
);
});
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: iconBrightness,
systemNavigationBarIconBrightness: iconBrightness,
systemNavigationBarColor: context.colorScheme.surface,
systemNavigationBarDividerColor: Colors.transparent,
),
sized: false,
child: child,
);
},
),
return Consumer(
builder: (context, ref, _) {
final brightness = ref.watch(currentBrightnessProvider);
final iconBrightness = brightness == Brightness.light
? Brightness.dark
: Brightness.light;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: iconBrightness,
systemNavigationBarIconBrightness: iconBrightness,
systemNavigationBarColor: Colors.transparent,
systemNavigationBarDividerColor: Colors.transparent,
systemNavigationBarContrastEnforced: false,
),
sized: false,
child: child,
);
},
);
}
+6 -3
View File
@@ -44,7 +44,7 @@ class _WindowContainerState extends ConsumerState<WindowManager>
@override
void onWindowClose() async {
await ref.read(systemActionProvider.notifier).handleBackOrExit();
await ref.read(systemActionProvider.notifier).handleClose();
super.onWindowClose();
}
@@ -178,7 +178,10 @@ class _WindowHeaderState extends State<WindowHeader> {
windowExtManager.setWindowCornerPreference(round: false);
}
}
isMaximizedNotifier.value = await windowManager.isMaximized();
final res = await windowManager.isMaximized();
if (mounted) {
isMaximizedNotifier.value = res;
}
}
Future<void> _updatePin() async {
@@ -226,7 +229,7 @@ class _WindowHeaderState extends State<WindowHeader> {
onPressed: () {
globalState.container
.read(systemActionProvider.notifier)
.handleBackOrExit();
.handleClose();
},
icon: const Icon(Icons.close),
),
+37 -39
View File
@@ -3,13 +3,23 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'generated/clash_config.freezed.dart';
part 'generated/clash_config.g.dart';
const defaultClashConfig = PatchClashConfig();
const defaultTun = Tun();
const defaultDns = Dns();
const defaultGeoXUrl = GeoXUrl();
const defaultGeoXUrl = {
GeoResource.MMDB:
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
GeoResource.ASN:
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb',
GeoResource.GEOIP:
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat',
GeoResource.GEOSITE:
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat',
};
const defaultMixedPort = 7890;
const defaultKeepAliveInterval = 30;
@@ -309,42 +319,6 @@ abstract class Dns with _$Dns {
}
}
@freezed
abstract class GeoXUrl with _$GeoXUrl {
const factory GeoXUrl({
@Default(
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
)
String mmdb,
@Default(
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb',
)
String asn,
@Default(
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat',
)
String geoip,
@Default(
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat',
)
String geosite,
}) = _GeoXUrl;
factory GeoXUrl.fromJson(Map<String, Object?> json) =>
_$GeoXUrlFromJson(json);
factory GeoXUrl.safeFormJson(Map<String, Object?>? json) {
if (json == null) {
return defaultGeoXUrl;
}
try {
return GeoXUrl.fromJson(json);
} catch (_) {
return defaultGeoXUrl;
}
}
}
@freezed
abstract class Rule with _$Rule {
const factory Rule({
@@ -542,6 +516,24 @@ abstract class ClashConfig with _$ClashConfig {
_$ClashConfigFromJson(json);
}
extension GeoResourceUrlMapExt on Map<GeoResource, String> {
Map<String, String> get raw =>
map((key, value) => MapEntry(key.value, value));
}
Map<GeoResource, String> _geoXUrlFromJson(Map<String, Object?>? json) {
if (json == null) {
return defaultGeoXUrl;
}
return json.map(
(key, value) => MapEntry(GeoResource.fromJson(key), value as String),
);
}
Map<String, String> _geoXUrlToJson(Map<GeoResource, String> value) {
return value.raw;
}
@freezed
abstract class PatchClashConfig with _$PatchClashConfig {
const factory PatchClashConfig({
@@ -568,8 +560,12 @@ abstract class PatchClashConfig with _$PatchClashConfig {
@Default(defaultTun) @JsonKey(fromJson: Tun.safeFormJson) Tun tun,
@Default(defaultDns) @JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns,
@Default(defaultGeoXUrl)
@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson)
GeoXUrl geoXUrl,
@JsonKey(
name: 'geox-url',
fromJson: _geoXUrlFromJson,
toJson: _geoXUrlToJson,
)
Map<GeoResource, String> geoXUrl,
@Default(GeodataLoader.memconservative)
@JsonKey(name: 'geodata-loader')
GeodataLoader geodataLoader,
@@ -578,6 +574,8 @@ abstract class PatchClashConfig with _$PatchClashConfig {
@JsonKey(name: 'external-controller')
ExternalControllerStatus externalController,
@Default({}) Map<String, String> hosts,
@Default(false) @JsonKey(name: 'geo-auto-update') bool geoAutoUpdate,
@Default(24) @JsonKey(name: 'geo-update-interval') int geoUpdateInterval,
}) = _PatchClashConfig;
factory PatchClashConfig.fromJson(Map<String, Object?> json) =>
+2 -1
View File
@@ -9,6 +9,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'clash_config.dart';
part 'generated/common.freezed.dart';
part 'generated/common.g.dart';
@freezed
@@ -271,7 +272,7 @@ extension TrafficExt on Traffic {
}
String get trayTitle {
return '${up.shortTraffic.show}/s \n ${down.shortTraffic.show}/s';
return '${up.shortTraffic.show}/s\n${down.shortTraffic.show}/s';
}
num get speed => up + down;
+1
View File
@@ -84,6 +84,7 @@ abstract class AppSettingProps with _$AppSettingProps {
@Default(false) bool developerMode,
@Default(RestoreStrategy.compatible) RestoreStrategy restoreStrategy,
@Default(true) bool showTrayTitle,
@Default('') String customUserAgent,
}) = _AppSettingProps;
factory AppSettingProps.fromJson(Map<String, Object?> json) =>
+2
View File
@@ -31,6 +31,8 @@ abstract class UpdateParams with _$UpdateParams {
@JsonKey(name: 'external-controller')
required ExternalControllerStatus externalController,
@JsonKey(name: 'unified-delay') required bool unifiedDelay,
@Default(false) @JsonKey(name: 'geo-auto-update') bool geoAutoUpdate,
@Default(24) @JsonKey(name: 'geo-update-interval') int geoUpdateInterval,
}) = _UpdateParams;
factory UpdateParams.fromJson(Map<String, dynamic> json) =>
+38 -316
View File
@@ -3000,278 +3000,6 @@ $FallbackFilterCopyWith<$Res> get fallbackFilter {
}
/// @nodoc
mixin _$GeoXUrl {
String get mmdb; String get asn; String get geoip; String get geosite;
/// Create a copy of GeoXUrl
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$GeoXUrlCopyWith<GeoXUrl> get copyWith => _$GeoXUrlCopyWithImpl<GeoXUrl>(this as GeoXUrl, _$identity);
/// Serializes this GeoXUrl to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is GeoXUrl&&(identical(other.mmdb, mmdb) || other.mmdb == mmdb)&&(identical(other.asn, asn) || other.asn == asn)&&(identical(other.geoip, geoip) || other.geoip == geoip)&&(identical(other.geosite, geosite) || other.geosite == geosite));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,mmdb,asn,geoip,geosite);
@override
String toString() {
return 'GeoXUrl(mmdb: $mmdb, asn: $asn, geoip: $geoip, geosite: $geosite)';
}
}
/// @nodoc
abstract mixin class $GeoXUrlCopyWith<$Res> {
factory $GeoXUrlCopyWith(GeoXUrl value, $Res Function(GeoXUrl) _then) = _$GeoXUrlCopyWithImpl;
@useResult
$Res call({
String mmdb, String asn, String geoip, String geosite
});
}
/// @nodoc
class _$GeoXUrlCopyWithImpl<$Res>
implements $GeoXUrlCopyWith<$Res> {
_$GeoXUrlCopyWithImpl(this._self, this._then);
final GeoXUrl _self;
final $Res Function(GeoXUrl) _then;
/// Create a copy of GeoXUrl
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? mmdb = null,Object? asn = null,Object? geoip = null,Object? geosite = null,}) {
return _then(_self.copyWith(
mmdb: null == mmdb ? _self.mmdb : mmdb // ignore: cast_nullable_to_non_nullable
as String,asn: null == asn ? _self.asn : asn // ignore: cast_nullable_to_non_nullable
as String,geoip: null == geoip ? _self.geoip : geoip // ignore: cast_nullable_to_non_nullable
as String,geosite: null == geosite ? _self.geosite : geosite // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [GeoXUrl].
extension GeoXUrlPatterns on GeoXUrl {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _GeoXUrl value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _GeoXUrl() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _GeoXUrl value) $default,){
final _that = this;
switch (_that) {
case _GeoXUrl():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _GeoXUrl value)? $default,){
final _that = this;
switch (_that) {
case _GeoXUrl() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String mmdb, String asn, String geoip, String geosite)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _GeoXUrl() when $default != null:
return $default(_that.mmdb,_that.asn,_that.geoip,_that.geosite);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String mmdb, String asn, String geoip, String geosite) $default,) {final _that = this;
switch (_that) {
case _GeoXUrl():
return $default(_that.mmdb,_that.asn,_that.geoip,_that.geosite);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String mmdb, String asn, String geoip, String geosite)? $default,) {final _that = this;
switch (_that) {
case _GeoXUrl() when $default != null:
return $default(_that.mmdb,_that.asn,_that.geoip,_that.geosite);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _GeoXUrl implements GeoXUrl {
const _GeoXUrl({this.mmdb = 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb', this.asn = 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb', this.geoip = 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat', this.geosite = 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat'});
factory _GeoXUrl.fromJson(Map<String, dynamic> json) => _$GeoXUrlFromJson(json);
@override@JsonKey() final String mmdb;
@override@JsonKey() final String asn;
@override@JsonKey() final String geoip;
@override@JsonKey() final String geosite;
/// Create a copy of GeoXUrl
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$GeoXUrlCopyWith<_GeoXUrl> get copyWith => __$GeoXUrlCopyWithImpl<_GeoXUrl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$GeoXUrlToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _GeoXUrl&&(identical(other.mmdb, mmdb) || other.mmdb == mmdb)&&(identical(other.asn, asn) || other.asn == asn)&&(identical(other.geoip, geoip) || other.geoip == geoip)&&(identical(other.geosite, geosite) || other.geosite == geosite));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,mmdb,asn,geoip,geosite);
@override
String toString() {
return 'GeoXUrl(mmdb: $mmdb, asn: $asn, geoip: $geoip, geosite: $geosite)';
}
}
/// @nodoc
abstract mixin class _$GeoXUrlCopyWith<$Res> implements $GeoXUrlCopyWith<$Res> {
factory _$GeoXUrlCopyWith(_GeoXUrl value, $Res Function(_GeoXUrl) _then) = __$GeoXUrlCopyWithImpl;
@override @useResult
$Res call({
String mmdb, String asn, String geoip, String geosite
});
}
/// @nodoc
class __$GeoXUrlCopyWithImpl<$Res>
implements _$GeoXUrlCopyWith<$Res> {
__$GeoXUrlCopyWithImpl(this._self, this._then);
final _GeoXUrl _self;
final $Res Function(_GeoXUrl) _then;
/// Create a copy of GeoXUrl
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? mmdb = null,Object? asn = null,Object? geoip = null,Object? geosite = null,}) {
return _then(_GeoXUrl(
mmdb: null == mmdb ? _self.mmdb : mmdb // ignore: cast_nullable_to_non_nullable
as String,asn: null == asn ? _self.asn : asn // ignore: cast_nullable_to_non_nullable
as String,geoip: null == geoip ? _self.geoip : geoip // ignore: cast_nullable_to_non_nullable
as String,geosite: null == geosite ? _self.geosite : geosite // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
mixin _$Rule {
@@ -3885,7 +3613,7 @@ as Map<String, String>,
/// @nodoc
mixin _$PatchClashConfig {
@JsonKey(name: 'mixed-port') int get mixedPort;@JsonKey(name: 'socks-port') int get socksPort;@JsonKey(name: 'port') int get port;@JsonKey(name: 'redir-port') int get redirPort;@JsonKey(name: 'tproxy-port') int get tproxyPort; Mode get mode;@JsonKey(name: 'allow-lan') bool get allowLan;@JsonKey(name: 'log-level') LogLevel get logLevel; bool get ipv6;@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode get findProcessMode;@JsonKey(name: 'keep-alive-interval') int get keepAliveInterval;@JsonKey(name: 'unified-delay') bool get unifiedDelay;@JsonKey(name: 'tcp-concurrent') bool get tcpConcurrent;@JsonKey(fromJson: Tun.safeFormJson) Tun get tun;@JsonKey(fromJson: Dns.safeDnsFromJson) Dns get dns;@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl get geoXUrl;@JsonKey(name: 'geodata-loader') GeodataLoader get geodataLoader;@JsonKey(name: 'global-ua') String? get globalUa;@JsonKey(name: 'external-controller') ExternalControllerStatus get externalController; Map<String, String> get hosts;
@JsonKey(name: 'mixed-port') int get mixedPort;@JsonKey(name: 'socks-port') int get socksPort;@JsonKey(name: 'port') int get port;@JsonKey(name: 'redir-port') int get redirPort;@JsonKey(name: 'tproxy-port') int get tproxyPort; Mode get mode;@JsonKey(name: 'allow-lan') bool get allowLan;@JsonKey(name: 'log-level') LogLevel get logLevel; bool get ipv6;@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode get findProcessMode;@JsonKey(name: 'keep-alive-interval') int get keepAliveInterval;@JsonKey(name: 'unified-delay') bool get unifiedDelay;@JsonKey(name: 'tcp-concurrent') bool get tcpConcurrent;@JsonKey(fromJson: Tun.safeFormJson) Tun get tun;@JsonKey(fromJson: Dns.safeDnsFromJson) Dns get dns;@JsonKey(name: 'geox-url', fromJson: _geoXUrlFromJson, toJson: _geoXUrlToJson) Map<GeoResource, String> get geoXUrl;@JsonKey(name: 'geodata-loader') GeodataLoader get geodataLoader;@JsonKey(name: 'global-ua') String? get globalUa;@JsonKey(name: 'external-controller') ExternalControllerStatus get externalController; Map<String, String> get hosts;@JsonKey(name: 'geo-auto-update') bool get geoAutoUpdate;@JsonKey(name: 'geo-update-interval') int get geoUpdateInterval;
/// Create a copy of PatchClashConfig
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -3898,16 +3626,16 @@ $PatchClashConfigCopyWith<PatchClashConfig> get copyWith => _$PatchClashConfigCo
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PatchClashConfig&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.socksPort, socksPort) || other.socksPort == socksPort)&&(identical(other.port, port) || other.port == port)&&(identical(other.redirPort, redirPort) || other.redirPort == redirPort)&&(identical(other.tproxyPort, tproxyPort) || other.tproxyPort == tproxyPort)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.keepAliveInterval, keepAliveInterval) || other.keepAliveInterval == keepAliveInterval)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.dns, dns) || other.dns == dns)&&(identical(other.geoXUrl, geoXUrl) || other.geoXUrl == geoXUrl)&&(identical(other.geodataLoader, geodataLoader) || other.geodataLoader == geodataLoader)&&(identical(other.globalUa, globalUa) || other.globalUa == globalUa)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&const DeepCollectionEquality().equals(other.hosts, hosts));
return identical(this, other) || (other.runtimeType == runtimeType&&other is PatchClashConfig&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.socksPort, socksPort) || other.socksPort == socksPort)&&(identical(other.port, port) || other.port == port)&&(identical(other.redirPort, redirPort) || other.redirPort == redirPort)&&(identical(other.tproxyPort, tproxyPort) || other.tproxyPort == tproxyPort)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.keepAliveInterval, keepAliveInterval) || other.keepAliveInterval == keepAliveInterval)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.dns, dns) || other.dns == dns)&&const DeepCollectionEquality().equals(other.geoXUrl, geoXUrl)&&(identical(other.geodataLoader, geodataLoader) || other.geodataLoader == geodataLoader)&&(identical(other.globalUa, globalUa) || other.globalUa == globalUa)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&const DeepCollectionEquality().equals(other.hosts, hosts)&&(identical(other.geoAutoUpdate, geoAutoUpdate) || other.geoAutoUpdate == geoAutoUpdate)&&(identical(other.geoUpdateInterval, geoUpdateInterval) || other.geoUpdateInterval == geoUpdateInterval));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hashAll([runtimeType,mixedPort,socksPort,port,redirPort,tproxyPort,mode,allowLan,logLevel,ipv6,findProcessMode,keepAliveInterval,unifiedDelay,tcpConcurrent,tun,dns,geoXUrl,geodataLoader,globalUa,externalController,const DeepCollectionEquality().hash(hosts)]);
int get hashCode => Object.hashAll([runtimeType,mixedPort,socksPort,port,redirPort,tproxyPort,mode,allowLan,logLevel,ipv6,findProcessMode,keepAliveInterval,unifiedDelay,tcpConcurrent,tun,dns,const DeepCollectionEquality().hash(geoXUrl),geodataLoader,globalUa,externalController,const DeepCollectionEquality().hash(hosts),geoAutoUpdate,geoUpdateInterval]);
@override
String toString() {
return 'PatchClashConfig(mixedPort: $mixedPort, socksPort: $socksPort, port: $port, redirPort: $redirPort, tproxyPort: $tproxyPort, mode: $mode, allowLan: $allowLan, logLevel: $logLevel, ipv6: $ipv6, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, geoXUrl: $geoXUrl, geodataLoader: $geodataLoader, globalUa: $globalUa, externalController: $externalController, hosts: $hosts)';
return 'PatchClashConfig(mixedPort: $mixedPort, socksPort: $socksPort, port: $port, redirPort: $redirPort, tproxyPort: $tproxyPort, mode: $mode, allowLan: $allowLan, logLevel: $logLevel, ipv6: $ipv6, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, geoXUrl: $geoXUrl, geodataLoader: $geodataLoader, globalUa: $globalUa, externalController: $externalController, hosts: $hosts, geoAutoUpdate: $geoAutoUpdate, geoUpdateInterval: $geoUpdateInterval)';
}
@@ -3918,11 +3646,11 @@ abstract mixin class $PatchClashConfigCopyWith<$Res> {
factory $PatchClashConfigCopyWith(PatchClashConfig value, $Res Function(PatchClashConfig) _then) = _$PatchClashConfigCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'socks-port') int socksPort,@JsonKey(name: 'port') int port,@JsonKey(name: 'redir-port') int redirPort,@JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode,@JsonKey(name: 'keep-alive-interval') int keepAliveInterval,@JsonKey(name: 'unified-delay') bool unifiedDelay,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(fromJson: Tun.safeFormJson) Tun tun,@JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns,@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl geoXUrl,@JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader,@JsonKey(name: 'global-ua') String? globalUa,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts
@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'socks-port') int socksPort,@JsonKey(name: 'port') int port,@JsonKey(name: 'redir-port') int redirPort,@JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode,@JsonKey(name: 'keep-alive-interval') int keepAliveInterval,@JsonKey(name: 'unified-delay') bool unifiedDelay,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(fromJson: Tun.safeFormJson) Tun tun,@JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns,@JsonKey(name: 'geox-url', fromJson: _geoXUrlFromJson, toJson: _geoXUrlToJson) Map<GeoResource, String> geoXUrl,@JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader,@JsonKey(name: 'global-ua') String? globalUa,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts,@JsonKey(name: 'geo-auto-update') bool geoAutoUpdate,@JsonKey(name: 'geo-update-interval') int geoUpdateInterval
});
$TunCopyWith<$Res> get tun;$DnsCopyWith<$Res> get dns;$GeoXUrlCopyWith<$Res> get geoXUrl;
$TunCopyWith<$Res> get tun;$DnsCopyWith<$Res> get dns;
}
/// @nodoc
@@ -3935,7 +3663,7 @@ class _$PatchClashConfigCopyWithImpl<$Res>
/// Create a copy of PatchClashConfig
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? mixedPort = null,Object? socksPort = null,Object? port = null,Object? redirPort = null,Object? tproxyPort = null,Object? mode = null,Object? allowLan = null,Object? logLevel = null,Object? ipv6 = null,Object? findProcessMode = null,Object? keepAliveInterval = null,Object? unifiedDelay = null,Object? tcpConcurrent = null,Object? tun = null,Object? dns = null,Object? geoXUrl = null,Object? geodataLoader = null,Object? globalUa = freezed,Object? externalController = null,Object? hosts = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? mixedPort = null,Object? socksPort = null,Object? port = null,Object? redirPort = null,Object? tproxyPort = null,Object? mode = null,Object? allowLan = null,Object? logLevel = null,Object? ipv6 = null,Object? findProcessMode = null,Object? keepAliveInterval = null,Object? unifiedDelay = null,Object? tcpConcurrent = null,Object? tun = null,Object? dns = null,Object? geoXUrl = null,Object? geodataLoader = null,Object? globalUa = freezed,Object? externalController = null,Object? hosts = null,Object? geoAutoUpdate = null,Object? geoUpdateInterval = null,}) {
return _then(_self.copyWith(
mixedPort: null == mixedPort ? _self.mixedPort : mixedPort // ignore: cast_nullable_to_non_nullable
as int,socksPort: null == socksPort ? _self.socksPort : socksPort // ignore: cast_nullable_to_non_nullable
@@ -3953,11 +3681,13 @@ as bool,tcpConcurrent: null == tcpConcurrent ? _self.tcpConcurrent : tcpConcurre
as bool,tun: null == tun ? _self.tun : tun // ignore: cast_nullable_to_non_nullable
as Tun,dns: null == dns ? _self.dns : dns // ignore: cast_nullable_to_non_nullable
as Dns,geoXUrl: null == geoXUrl ? _self.geoXUrl : geoXUrl // ignore: cast_nullable_to_non_nullable
as GeoXUrl,geodataLoader: null == geodataLoader ? _self.geodataLoader : geodataLoader // ignore: cast_nullable_to_non_nullable
as Map<GeoResource, String>,geodataLoader: null == geodataLoader ? _self.geodataLoader : geodataLoader // ignore: cast_nullable_to_non_nullable
as GeodataLoader,globalUa: freezed == globalUa ? _self.globalUa : globalUa // ignore: cast_nullable_to_non_nullable
as String?,externalController: null == externalController ? _self.externalController : externalController // ignore: cast_nullable_to_non_nullable
as ExternalControllerStatus,hosts: null == hosts ? _self.hosts : hosts // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
as Map<String, String>,geoAutoUpdate: null == geoAutoUpdate ? _self.geoAutoUpdate : geoAutoUpdate // ignore: cast_nullable_to_non_nullable
as bool,geoUpdateInterval: null == geoUpdateInterval ? _self.geoUpdateInterval : geoUpdateInterval // ignore: cast_nullable_to_non_nullable
as int,
));
}
/// Create a copy of PatchClashConfig
@@ -3978,15 +3708,6 @@ $DnsCopyWith<$Res> get dns {
return $DnsCopyWith<$Res>(_self.dns, (value) {
return _then(_self.copyWith(dns: value));
});
}/// Create a copy of PatchClashConfig
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$GeoXUrlCopyWith<$Res> get geoXUrl {
return $GeoXUrlCopyWith<$Res>(_self.geoXUrl, (value) {
return _then(_self.copyWith(geoXUrl: value));
});
}
}
@@ -4069,10 +3790,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'socks-port') int socksPort, @JsonKey(name: 'port') int port, @JsonKey(name: 'redir-port') int redirPort, @JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval') int keepAliveInterval, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson) Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl geoXUrl, @JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader, @JsonKey(name: 'global-ua') String? globalUa, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'socks-port') int socksPort, @JsonKey(name: 'port') int port, @JsonKey(name: 'redir-port') int redirPort, @JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval') int keepAliveInterval, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson) Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns, @JsonKey(name: 'geox-url', fromJson: _geoXUrlFromJson, toJson: _geoXUrlToJson) Map<GeoResource, String> geoXUrl, @JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader, @JsonKey(name: 'global-ua') String? globalUa, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts, @JsonKey(name: 'geo-auto-update') bool geoAutoUpdate, @JsonKey(name: 'geo-update-interval') int geoUpdateInterval)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PatchClashConfig() when $default != null:
return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.geoXUrl,_that.geodataLoader,_that.globalUa,_that.externalController,_that.hosts);case _:
return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.geoXUrl,_that.geodataLoader,_that.globalUa,_that.externalController,_that.hosts,_that.geoAutoUpdate,_that.geoUpdateInterval);case _:
return orElse();
}
@@ -4090,10 +3811,10 @@ return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'socks-port') int socksPort, @JsonKey(name: 'port') int port, @JsonKey(name: 'redir-port') int redirPort, @JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval') int keepAliveInterval, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson) Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl geoXUrl, @JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader, @JsonKey(name: 'global-ua') String? globalUa, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'socks-port') int socksPort, @JsonKey(name: 'port') int port, @JsonKey(name: 'redir-port') int redirPort, @JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval') int keepAliveInterval, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson) Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns, @JsonKey(name: 'geox-url', fromJson: _geoXUrlFromJson, toJson: _geoXUrlToJson) Map<GeoResource, String> geoXUrl, @JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader, @JsonKey(name: 'global-ua') String? globalUa, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts, @JsonKey(name: 'geo-auto-update') bool geoAutoUpdate, @JsonKey(name: 'geo-update-interval') int geoUpdateInterval) $default,) {final _that = this;
switch (_that) {
case _PatchClashConfig():
return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.geoXUrl,_that.geodataLoader,_that.globalUa,_that.externalController,_that.hosts);case _:
return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.geoXUrl,_that.geodataLoader,_that.globalUa,_that.externalController,_that.hosts,_that.geoAutoUpdate,_that.geoUpdateInterval);case _:
throw StateError('Unexpected subclass');
}
@@ -4110,10 +3831,10 @@ return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'socks-port') int socksPort, @JsonKey(name: 'port') int port, @JsonKey(name: 'redir-port') int redirPort, @JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval') int keepAliveInterval, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson) Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl geoXUrl, @JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader, @JsonKey(name: 'global-ua') String? globalUa, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'socks-port') int socksPort, @JsonKey(name: 'port') int port, @JsonKey(name: 'redir-port') int redirPort, @JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval') int keepAliveInterval, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson) Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns, @JsonKey(name: 'geox-url', fromJson: _geoXUrlFromJson, toJson: _geoXUrlToJson) Map<GeoResource, String> geoXUrl, @JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader, @JsonKey(name: 'global-ua') String? globalUa, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts, @JsonKey(name: 'geo-auto-update') bool geoAutoUpdate, @JsonKey(name: 'geo-update-interval') int geoUpdateInterval)? $default,) {final _that = this;
switch (_that) {
case _PatchClashConfig() when $default != null:
return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.geoXUrl,_that.geodataLoader,_that.globalUa,_that.externalController,_that.hosts);case _:
return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.geoXUrl,_that.geodataLoader,_that.globalUa,_that.externalController,_that.hosts,_that.geoAutoUpdate,_that.geoUpdateInterval);case _:
return null;
}
@@ -4125,7 +3846,7 @@ return $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that
@JsonSerializable()
class _PatchClashConfig implements PatchClashConfig {
const _PatchClashConfig({@JsonKey(name: 'mixed-port') this.mixedPort = defaultMixedPort, @JsonKey(name: 'socks-port') this.socksPort = 0, @JsonKey(name: 'port') this.port = 0, @JsonKey(name: 'redir-port') this.redirPort = 0, @JsonKey(name: 'tproxy-port') this.tproxyPort = 0, this.mode = Mode.rule, @JsonKey(name: 'allow-lan') this.allowLan = false, @JsonKey(name: 'log-level') this.logLevel = LogLevel.error, this.ipv6 = false, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) this.findProcessMode = FindProcessMode.always, @JsonKey(name: 'keep-alive-interval') this.keepAliveInterval = defaultKeepAliveInterval, @JsonKey(name: 'unified-delay') this.unifiedDelay = true, @JsonKey(name: 'tcp-concurrent') this.tcpConcurrent = true, @JsonKey(fromJson: Tun.safeFormJson) this.tun = defaultTun, @JsonKey(fromJson: Dns.safeDnsFromJson) this.dns = defaultDns, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) this.geoXUrl = defaultGeoXUrl, @JsonKey(name: 'geodata-loader') this.geodataLoader = GeodataLoader.memconservative, @JsonKey(name: 'global-ua') this.globalUa, @JsonKey(name: 'external-controller') this.externalController = ExternalControllerStatus.close, final Map<String, String> hosts = const {}}): _hosts = hosts;
const _PatchClashConfig({@JsonKey(name: 'mixed-port') this.mixedPort = defaultMixedPort, @JsonKey(name: 'socks-port') this.socksPort = 0, @JsonKey(name: 'port') this.port = 0, @JsonKey(name: 'redir-port') this.redirPort = 0, @JsonKey(name: 'tproxy-port') this.tproxyPort = 0, this.mode = Mode.rule, @JsonKey(name: 'allow-lan') this.allowLan = false, @JsonKey(name: 'log-level') this.logLevel = LogLevel.error, this.ipv6 = false, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) this.findProcessMode = FindProcessMode.always, @JsonKey(name: 'keep-alive-interval') this.keepAliveInterval = defaultKeepAliveInterval, @JsonKey(name: 'unified-delay') this.unifiedDelay = true, @JsonKey(name: 'tcp-concurrent') this.tcpConcurrent = true, @JsonKey(fromJson: Tun.safeFormJson) this.tun = defaultTun, @JsonKey(fromJson: Dns.safeDnsFromJson) this.dns = defaultDns, @JsonKey(name: 'geox-url', fromJson: _geoXUrlFromJson, toJson: _geoXUrlToJson) final Map<GeoResource, String> geoXUrl = defaultGeoXUrl, @JsonKey(name: 'geodata-loader') this.geodataLoader = GeodataLoader.memconservative, @JsonKey(name: 'global-ua') this.globalUa, @JsonKey(name: 'external-controller') this.externalController = ExternalControllerStatus.close, final Map<String, String> hosts = const {}, @JsonKey(name: 'geo-auto-update') this.geoAutoUpdate = false, @JsonKey(name: 'geo-update-interval') this.geoUpdateInterval = 24}): _geoXUrl = geoXUrl,_hosts = hosts;
factory _PatchClashConfig.fromJson(Map<String, dynamic> json) => _$PatchClashConfigFromJson(json);
@override@JsonKey(name: 'mixed-port') final int mixedPort;
@@ -4143,7 +3864,13 @@ class _PatchClashConfig implements PatchClashConfig {
@override@JsonKey(name: 'tcp-concurrent') final bool tcpConcurrent;
@override@JsonKey(fromJson: Tun.safeFormJson) final Tun tun;
@override@JsonKey(fromJson: Dns.safeDnsFromJson) final Dns dns;
@override@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) final GeoXUrl geoXUrl;
final Map<GeoResource, String> _geoXUrl;
@override@JsonKey(name: 'geox-url', fromJson: _geoXUrlFromJson, toJson: _geoXUrlToJson) Map<GeoResource, String> get geoXUrl {
if (_geoXUrl is EqualUnmodifiableMapView) return _geoXUrl;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_geoXUrl);
}
@override@JsonKey(name: 'geodata-loader') final GeodataLoader geodataLoader;
@override@JsonKey(name: 'global-ua') final String? globalUa;
@override@JsonKey(name: 'external-controller') final ExternalControllerStatus externalController;
@@ -4154,6 +3881,8 @@ class _PatchClashConfig implements PatchClashConfig {
return EqualUnmodifiableMapView(_hosts);
}
@override@JsonKey(name: 'geo-auto-update') final bool geoAutoUpdate;
@override@JsonKey(name: 'geo-update-interval') final int geoUpdateInterval;
/// Create a copy of PatchClashConfig
/// with the given fields replaced by the non-null parameter values.
@@ -4168,16 +3897,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PatchClashConfig&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.socksPort, socksPort) || other.socksPort == socksPort)&&(identical(other.port, port) || other.port == port)&&(identical(other.redirPort, redirPort) || other.redirPort == redirPort)&&(identical(other.tproxyPort, tproxyPort) || other.tproxyPort == tproxyPort)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.keepAliveInterval, keepAliveInterval) || other.keepAliveInterval == keepAliveInterval)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.dns, dns) || other.dns == dns)&&(identical(other.geoXUrl, geoXUrl) || other.geoXUrl == geoXUrl)&&(identical(other.geodataLoader, geodataLoader) || other.geodataLoader == geodataLoader)&&(identical(other.globalUa, globalUa) || other.globalUa == globalUa)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&const DeepCollectionEquality().equals(other._hosts, _hosts));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PatchClashConfig&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.socksPort, socksPort) || other.socksPort == socksPort)&&(identical(other.port, port) || other.port == port)&&(identical(other.redirPort, redirPort) || other.redirPort == redirPort)&&(identical(other.tproxyPort, tproxyPort) || other.tproxyPort == tproxyPort)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.keepAliveInterval, keepAliveInterval) || other.keepAliveInterval == keepAliveInterval)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.dns, dns) || other.dns == dns)&&const DeepCollectionEquality().equals(other._geoXUrl, _geoXUrl)&&(identical(other.geodataLoader, geodataLoader) || other.geodataLoader == geodataLoader)&&(identical(other.globalUa, globalUa) || other.globalUa == globalUa)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&const DeepCollectionEquality().equals(other._hosts, _hosts)&&(identical(other.geoAutoUpdate, geoAutoUpdate) || other.geoAutoUpdate == geoAutoUpdate)&&(identical(other.geoUpdateInterval, geoUpdateInterval) || other.geoUpdateInterval == geoUpdateInterval));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hashAll([runtimeType,mixedPort,socksPort,port,redirPort,tproxyPort,mode,allowLan,logLevel,ipv6,findProcessMode,keepAliveInterval,unifiedDelay,tcpConcurrent,tun,dns,geoXUrl,geodataLoader,globalUa,externalController,const DeepCollectionEquality().hash(_hosts)]);
int get hashCode => Object.hashAll([runtimeType,mixedPort,socksPort,port,redirPort,tproxyPort,mode,allowLan,logLevel,ipv6,findProcessMode,keepAliveInterval,unifiedDelay,tcpConcurrent,tun,dns,const DeepCollectionEquality().hash(_geoXUrl),geodataLoader,globalUa,externalController,const DeepCollectionEquality().hash(_hosts),geoAutoUpdate,geoUpdateInterval]);
@override
String toString() {
return 'PatchClashConfig(mixedPort: $mixedPort, socksPort: $socksPort, port: $port, redirPort: $redirPort, tproxyPort: $tproxyPort, mode: $mode, allowLan: $allowLan, logLevel: $logLevel, ipv6: $ipv6, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, geoXUrl: $geoXUrl, geodataLoader: $geodataLoader, globalUa: $globalUa, externalController: $externalController, hosts: $hosts)';
return 'PatchClashConfig(mixedPort: $mixedPort, socksPort: $socksPort, port: $port, redirPort: $redirPort, tproxyPort: $tproxyPort, mode: $mode, allowLan: $allowLan, logLevel: $logLevel, ipv6: $ipv6, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, geoXUrl: $geoXUrl, geodataLoader: $geodataLoader, globalUa: $globalUa, externalController: $externalController, hosts: $hosts, geoAutoUpdate: $geoAutoUpdate, geoUpdateInterval: $geoUpdateInterval)';
}
@@ -4188,11 +3917,11 @@ abstract mixin class _$PatchClashConfigCopyWith<$Res> implements $PatchClashConf
factory _$PatchClashConfigCopyWith(_PatchClashConfig value, $Res Function(_PatchClashConfig) _then) = __$PatchClashConfigCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'socks-port') int socksPort,@JsonKey(name: 'port') int port,@JsonKey(name: 'redir-port') int redirPort,@JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode,@JsonKey(name: 'keep-alive-interval') int keepAliveInterval,@JsonKey(name: 'unified-delay') bool unifiedDelay,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(fromJson: Tun.safeFormJson) Tun tun,@JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns,@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl geoXUrl,@JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader,@JsonKey(name: 'global-ua') String? globalUa,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts
@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'socks-port') int socksPort,@JsonKey(name: 'port') int port,@JsonKey(name: 'redir-port') int redirPort,@JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode,@JsonKey(name: 'keep-alive-interval') int keepAliveInterval,@JsonKey(name: 'unified-delay') bool unifiedDelay,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(fromJson: Tun.safeFormJson) Tun tun,@JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns,@JsonKey(name: 'geox-url', fromJson: _geoXUrlFromJson, toJson: _geoXUrlToJson) Map<GeoResource, String> geoXUrl,@JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader,@JsonKey(name: 'global-ua') String? globalUa,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController, Map<String, String> hosts,@JsonKey(name: 'geo-auto-update') bool geoAutoUpdate,@JsonKey(name: 'geo-update-interval') int geoUpdateInterval
});
@override $TunCopyWith<$Res> get tun;@override $DnsCopyWith<$Res> get dns;@override $GeoXUrlCopyWith<$Res> get geoXUrl;
@override $TunCopyWith<$Res> get tun;@override $DnsCopyWith<$Res> get dns;
}
/// @nodoc
@@ -4205,7 +3934,7 @@ class __$PatchClashConfigCopyWithImpl<$Res>
/// Create a copy of PatchClashConfig
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? mixedPort = null,Object? socksPort = null,Object? port = null,Object? redirPort = null,Object? tproxyPort = null,Object? mode = null,Object? allowLan = null,Object? logLevel = null,Object? ipv6 = null,Object? findProcessMode = null,Object? keepAliveInterval = null,Object? unifiedDelay = null,Object? tcpConcurrent = null,Object? tun = null,Object? dns = null,Object? geoXUrl = null,Object? geodataLoader = null,Object? globalUa = freezed,Object? externalController = null,Object? hosts = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? mixedPort = null,Object? socksPort = null,Object? port = null,Object? redirPort = null,Object? tproxyPort = null,Object? mode = null,Object? allowLan = null,Object? logLevel = null,Object? ipv6 = null,Object? findProcessMode = null,Object? keepAliveInterval = null,Object? unifiedDelay = null,Object? tcpConcurrent = null,Object? tun = null,Object? dns = null,Object? geoXUrl = null,Object? geodataLoader = null,Object? globalUa = freezed,Object? externalController = null,Object? hosts = null,Object? geoAutoUpdate = null,Object? geoUpdateInterval = null,}) {
return _then(_PatchClashConfig(
mixedPort: null == mixedPort ? _self.mixedPort : mixedPort // ignore: cast_nullable_to_non_nullable
as int,socksPort: null == socksPort ? _self.socksPort : socksPort // ignore: cast_nullable_to_non_nullable
@@ -4222,12 +3951,14 @@ as int,unifiedDelay: null == unifiedDelay ? _self.unifiedDelay : unifiedDelay //
as bool,tcpConcurrent: null == tcpConcurrent ? _self.tcpConcurrent : tcpConcurrent // ignore: cast_nullable_to_non_nullable
as bool,tun: null == tun ? _self.tun : tun // ignore: cast_nullable_to_non_nullable
as Tun,dns: null == dns ? _self.dns : dns // ignore: cast_nullable_to_non_nullable
as Dns,geoXUrl: null == geoXUrl ? _self.geoXUrl : geoXUrl // ignore: cast_nullable_to_non_nullable
as GeoXUrl,geodataLoader: null == geodataLoader ? _self.geodataLoader : geodataLoader // ignore: cast_nullable_to_non_nullable
as Dns,geoXUrl: null == geoXUrl ? _self._geoXUrl : geoXUrl // ignore: cast_nullable_to_non_nullable
as Map<GeoResource, String>,geodataLoader: null == geodataLoader ? _self.geodataLoader : geodataLoader // ignore: cast_nullable_to_non_nullable
as GeodataLoader,globalUa: freezed == globalUa ? _self.globalUa : globalUa // ignore: cast_nullable_to_non_nullable
as String?,externalController: null == externalController ? _self.externalController : externalController // ignore: cast_nullable_to_non_nullable
as ExternalControllerStatus,hosts: null == hosts ? _self._hosts : hosts // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
as Map<String, String>,geoAutoUpdate: null == geoAutoUpdate ? _self.geoAutoUpdate : geoAutoUpdate // ignore: cast_nullable_to_non_nullable
as bool,geoUpdateInterval: null == geoUpdateInterval ? _self.geoUpdateInterval : geoUpdateInterval // ignore: cast_nullable_to_non_nullable
as int,
));
}
@@ -4249,15 +3980,6 @@ $DnsCopyWith<$Res> get dns {
return $DnsCopyWith<$Res>(_self.dns, (value) {
return _then(_self.copyWith(dns: value));
});
}/// Create a copy of PatchClashConfig
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$GeoXUrlCopyWith<$Res> get geoXUrl {
return $GeoXUrlCopyWith<$Res>(_self.geoXUrl, (value) {
return _then(_self.copyWith(geoXUrl: value));
});
}
}
+6 -24
View File
@@ -295,28 +295,6 @@ const _$DnsModeEnumMap = {
DnsMode.hosts: 'hosts',
};
_GeoXUrl _$GeoXUrlFromJson(Map<String, dynamic> json) => _GeoXUrl(
mmdb:
json['mmdb'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
asn:
json['asn'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb',
geoip:
json['geoip'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat',
geosite:
json['geosite'] as String? ??
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat',
);
Map<String, dynamic> _$GeoXUrlToJson(_GeoXUrl instance) => <String, dynamic>{
'mmdb': instance.mmdb,
'asn': instance.asn,
'geoip': instance.geoip,
'geosite': instance.geosite,
};
_Rule _$RuleFromJson(Map<String, dynamic> json) => _Rule(
id: (json['id'] as num?)?.toInt() ?? -1,
ruleAction:
@@ -451,7 +429,7 @@ _PatchClashConfig _$PatchClashConfigFromJson(Map<String, dynamic> json) =>
: Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>),
geoXUrl: json['geox-url'] == null
? defaultGeoXUrl
: GeoXUrl.safeFormJson(json['geox-url'] as Map<String, Object?>?),
: _geoXUrlFromJson(json['geox-url'] as Map<String, Object?>?),
geodataLoader:
$enumDecodeNullable(_$GeodataLoaderEnumMap, json['geodata-loader']) ??
GeodataLoader.memconservative,
@@ -467,6 +445,8 @@ _PatchClashConfig _$PatchClashConfigFromJson(Map<String, dynamic> json) =>
(k, e) => MapEntry(k, e as String),
) ??
const {},
geoAutoUpdate: json['geo-auto-update'] as bool? ?? false,
geoUpdateInterval: (json['geo-update-interval'] as num?)?.toInt() ?? 24,
);
Map<String, dynamic> _$PatchClashConfigToJson(_PatchClashConfig instance) =>
@@ -486,12 +466,14 @@ Map<String, dynamic> _$PatchClashConfigToJson(_PatchClashConfig instance) =>
'tcp-concurrent': instance.tcpConcurrent,
'tun': instance.tun,
'dns': instance.dns,
'geox-url': instance.geoXUrl,
'geox-url': _geoXUrlToJson(instance.geoXUrl),
'geodata-loader': _$GeodataLoaderEnumMap[instance.geodataLoader]!,
'global-ua': instance.globalUa,
'external-controller':
_$ExternalControllerStatusEnumMap[instance.externalController]!,
'hosts': instance.hosts,
'geo-auto-update': instance.geoAutoUpdate,
'geo-update-interval': instance.geoUpdateInterval,
};
const _$ModeEnumMap = {
+23 -20
View File
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$AppSettingProps {
String? get locale;@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> get dashboardWidgets; bool get onlyStatisticsProxy; bool get autoLaunch; bool get silentLaunch; bool get autoRun; bool get openLogs; bool get closeConnections; String get testUrl; bool get isAnimateToPage; bool get autoCheckUpdate; bool get showLabel; bool get disclaimerAccepted; bool get crashlyticsTip; bool get crashlytics; bool get minimizeOnExit; bool get hidden; bool get developerMode; RestoreStrategy get restoreStrategy; bool get showTrayTitle;
String? get locale;@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> get dashboardWidgets; bool get onlyStatisticsProxy; bool get autoLaunch; bool get silentLaunch; bool get autoRun; bool get openLogs; bool get closeConnections; String get testUrl; bool get isAnimateToPage; bool get autoCheckUpdate; bool get showLabel; bool get disclaimerAccepted; bool get crashlyticsTip; bool get crashlytics; bool get minimizeOnExit; bool get hidden; bool get developerMode; RestoreStrategy get restoreStrategy; bool get showTrayTitle; String get customUserAgent;
/// Create a copy of AppSettingProps
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $AppSettingPropsCopyWith<AppSettingProps> get copyWith => _$AppSettingPropsCopyW
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettingProps&&(identical(other.locale, locale) || other.locale == locale)&&const DeepCollectionEquality().equals(other.dashboardWidgets, dashboardWidgets)&&(identical(other.onlyStatisticsProxy, onlyStatisticsProxy) || other.onlyStatisticsProxy == onlyStatisticsProxy)&&(identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch)&&(identical(other.silentLaunch, silentLaunch) || other.silentLaunch == silentLaunch)&&(identical(other.autoRun, autoRun) || other.autoRun == autoRun)&&(identical(other.openLogs, openLogs) || other.openLogs == openLogs)&&(identical(other.closeConnections, closeConnections) || other.closeConnections == closeConnections)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.isAnimateToPage, isAnimateToPage) || other.isAnimateToPage == isAnimateToPage)&&(identical(other.autoCheckUpdate, autoCheckUpdate) || other.autoCheckUpdate == autoCheckUpdate)&&(identical(other.showLabel, showLabel) || other.showLabel == showLabel)&&(identical(other.disclaimerAccepted, disclaimerAccepted) || other.disclaimerAccepted == disclaimerAccepted)&&(identical(other.crashlyticsTip, crashlyticsTip) || other.crashlyticsTip == crashlyticsTip)&&(identical(other.crashlytics, crashlytics) || other.crashlytics == crashlytics)&&(identical(other.minimizeOnExit, minimizeOnExit) || other.minimizeOnExit == minimizeOnExit)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.developerMode, developerMode) || other.developerMode == developerMode)&&(identical(other.restoreStrategy, restoreStrategy) || other.restoreStrategy == restoreStrategy)&&(identical(other.showTrayTitle, showTrayTitle) || other.showTrayTitle == showTrayTitle));
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettingProps&&(identical(other.locale, locale) || other.locale == locale)&&const DeepCollectionEquality().equals(other.dashboardWidgets, dashboardWidgets)&&(identical(other.onlyStatisticsProxy, onlyStatisticsProxy) || other.onlyStatisticsProxy == onlyStatisticsProxy)&&(identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch)&&(identical(other.silentLaunch, silentLaunch) || other.silentLaunch == silentLaunch)&&(identical(other.autoRun, autoRun) || other.autoRun == autoRun)&&(identical(other.openLogs, openLogs) || other.openLogs == openLogs)&&(identical(other.closeConnections, closeConnections) || other.closeConnections == closeConnections)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.isAnimateToPage, isAnimateToPage) || other.isAnimateToPage == isAnimateToPage)&&(identical(other.autoCheckUpdate, autoCheckUpdate) || other.autoCheckUpdate == autoCheckUpdate)&&(identical(other.showLabel, showLabel) || other.showLabel == showLabel)&&(identical(other.disclaimerAccepted, disclaimerAccepted) || other.disclaimerAccepted == disclaimerAccepted)&&(identical(other.crashlyticsTip, crashlyticsTip) || other.crashlyticsTip == crashlyticsTip)&&(identical(other.crashlytics, crashlytics) || other.crashlytics == crashlytics)&&(identical(other.minimizeOnExit, minimizeOnExit) || other.minimizeOnExit == minimizeOnExit)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.developerMode, developerMode) || other.developerMode == developerMode)&&(identical(other.restoreStrategy, restoreStrategy) || other.restoreStrategy == restoreStrategy)&&(identical(other.showTrayTitle, showTrayTitle) || other.showTrayTitle == showTrayTitle)&&(identical(other.customUserAgent, customUserAgent) || other.customUserAgent == customUserAgent));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hashAll([runtimeType,locale,const DeepCollectionEquality().hash(dashboardWidgets),onlyStatisticsProxy,autoLaunch,silentLaunch,autoRun,openLogs,closeConnections,testUrl,isAnimateToPage,autoCheckUpdate,showLabel,disclaimerAccepted,crashlyticsTip,crashlytics,minimizeOnExit,hidden,developerMode,restoreStrategy,showTrayTitle]);
int get hashCode => Object.hashAll([runtimeType,locale,const DeepCollectionEquality().hash(dashboardWidgets),onlyStatisticsProxy,autoLaunch,silentLaunch,autoRun,openLogs,closeConnections,testUrl,isAnimateToPage,autoCheckUpdate,showLabel,disclaimerAccepted,crashlyticsTip,crashlytics,minimizeOnExit,hidden,developerMode,restoreStrategy,showTrayTitle,customUserAgent]);
@override
String toString() {
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, crashlyticsTip: $crashlyticsTip, crashlytics: $crashlytics, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode, restoreStrategy: $restoreStrategy, showTrayTitle: $showTrayTitle)';
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, crashlyticsTip: $crashlyticsTip, crashlytics: $crashlytics, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode, restoreStrategy: $restoreStrategy, showTrayTitle: $showTrayTitle, customUserAgent: $customUserAgent)';
}
@@ -48,7 +48,7 @@ abstract mixin class $AppSettingPropsCopyWith<$Res> {
factory $AppSettingPropsCopyWith(AppSettingProps value, $Res Function(AppSettingProps) _then) = _$AppSettingPropsCopyWithImpl;
@useResult
$Res call({
String? locale,@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle
String? locale,@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle, String customUserAgent
});
@@ -65,7 +65,7 @@ class _$AppSettingPropsCopyWithImpl<$Res>
/// Create a copy of AppSettingProps
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? locale = freezed,Object? dashboardWidgets = null,Object? onlyStatisticsProxy = null,Object? autoLaunch = null,Object? silentLaunch = null,Object? autoRun = null,Object? openLogs = null,Object? closeConnections = null,Object? testUrl = null,Object? isAnimateToPage = null,Object? autoCheckUpdate = null,Object? showLabel = null,Object? disclaimerAccepted = null,Object? crashlyticsTip = null,Object? crashlytics = null,Object? minimizeOnExit = null,Object? hidden = null,Object? developerMode = null,Object? restoreStrategy = null,Object? showTrayTitle = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? locale = freezed,Object? dashboardWidgets = null,Object? onlyStatisticsProxy = null,Object? autoLaunch = null,Object? silentLaunch = null,Object? autoRun = null,Object? openLogs = null,Object? closeConnections = null,Object? testUrl = null,Object? isAnimateToPage = null,Object? autoCheckUpdate = null,Object? showLabel = null,Object? disclaimerAccepted = null,Object? crashlyticsTip = null,Object? crashlytics = null,Object? minimizeOnExit = null,Object? hidden = null,Object? developerMode = null,Object? restoreStrategy = null,Object? showTrayTitle = null,Object? customUserAgent = null,}) {
return _then(_self.copyWith(
locale: freezed == locale ? _self.locale : locale // ignore: cast_nullable_to_non_nullable
as String?,dashboardWidgets: null == dashboardWidgets ? _self.dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable
@@ -87,7 +87,8 @@ as bool,hidden: null == hidden ? _self.hidden : hidden // ignore: cast_nullable_
as bool,developerMode: null == developerMode ? _self.developerMode : developerMode // ignore: cast_nullable_to_non_nullable
as bool,restoreStrategy: null == restoreStrategy ? _self.restoreStrategy : restoreStrategy // ignore: cast_nullable_to_non_nullable
as RestoreStrategy,showTrayTitle: null == showTrayTitle ? _self.showTrayTitle : showTrayTitle // ignore: cast_nullable_to_non_nullable
as bool,
as bool,customUserAgent: null == customUserAgent ? _self.customUserAgent : customUserAgent // ignore: cast_nullable_to_non_nullable
as String,
));
}
@@ -172,10 +173,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle, String customUserAgent)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _AppSettingProps() when $default != null:
return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.crashlyticsTip,_that.crashlytics,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.restoreStrategy,_that.showTrayTitle);case _:
return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.crashlyticsTip,_that.crashlytics,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.restoreStrategy,_that.showTrayTitle,_that.customUserAgent);case _:
return orElse();
}
@@ -193,10 +194,10 @@ return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_t
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle, String customUserAgent) $default,) {final _that = this;
switch (_that) {
case _AppSettingProps():
return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.crashlyticsTip,_that.crashlytics,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.restoreStrategy,_that.showTrayTitle);case _:
return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.crashlyticsTip,_that.crashlytics,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.restoreStrategy,_that.showTrayTitle,_that.customUserAgent);case _:
throw StateError('Unexpected subclass');
}
@@ -213,10 +214,10 @@ return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_t
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle, String customUserAgent)? $default,) {final _that = this;
switch (_that) {
case _AppSettingProps() when $default != null:
return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.crashlyticsTip,_that.crashlytics,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.restoreStrategy,_that.showTrayTitle);case _:
return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.crashlyticsTip,_that.crashlytics,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.restoreStrategy,_that.showTrayTitle,_that.customUserAgent);case _:
return null;
}
@@ -228,7 +229,7 @@ return $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_t
@JsonSerializable()
class _AppSettingProps implements AppSettingProps {
const _AppSettingProps({this.locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) final List<DashboardWidget> dashboardWidgets = defaultDashboardWidgets, this.onlyStatisticsProxy = false, this.autoLaunch = false, this.silentLaunch = false, this.autoRun = false, this.openLogs = false, this.closeConnections = true, this.testUrl = defaultTestUrl, this.isAnimateToPage = true, this.autoCheckUpdate = true, this.showLabel = false, this.disclaimerAccepted = false, this.crashlyticsTip = false, this.crashlytics = false, this.minimizeOnExit = true, this.hidden = false, this.developerMode = false, this.restoreStrategy = RestoreStrategy.compatible, this.showTrayTitle = true}): _dashboardWidgets = dashboardWidgets;
const _AppSettingProps({this.locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) final List<DashboardWidget> dashboardWidgets = defaultDashboardWidgets, this.onlyStatisticsProxy = false, this.autoLaunch = false, this.silentLaunch = false, this.autoRun = false, this.openLogs = false, this.closeConnections = true, this.testUrl = defaultTestUrl, this.isAnimateToPage = true, this.autoCheckUpdate = true, this.showLabel = false, this.disclaimerAccepted = false, this.crashlyticsTip = false, this.crashlytics = false, this.minimizeOnExit = true, this.hidden = false, this.developerMode = false, this.restoreStrategy = RestoreStrategy.compatible, this.showTrayTitle = true, this.customUserAgent = ''}): _dashboardWidgets = dashboardWidgets;
factory _AppSettingProps.fromJson(Map<String, dynamic> json) => _$AppSettingPropsFromJson(json);
@override final String? locale;
@@ -257,6 +258,7 @@ class _AppSettingProps implements AppSettingProps {
@override@JsonKey() final bool developerMode;
@override@JsonKey() final RestoreStrategy restoreStrategy;
@override@JsonKey() final bool showTrayTitle;
@override@JsonKey() final String customUserAgent;
/// Create a copy of AppSettingProps
/// with the given fields replaced by the non-null parameter values.
@@ -271,16 +273,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettingProps&&(identical(other.locale, locale) || other.locale == locale)&&const DeepCollectionEquality().equals(other._dashboardWidgets, _dashboardWidgets)&&(identical(other.onlyStatisticsProxy, onlyStatisticsProxy) || other.onlyStatisticsProxy == onlyStatisticsProxy)&&(identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch)&&(identical(other.silentLaunch, silentLaunch) || other.silentLaunch == silentLaunch)&&(identical(other.autoRun, autoRun) || other.autoRun == autoRun)&&(identical(other.openLogs, openLogs) || other.openLogs == openLogs)&&(identical(other.closeConnections, closeConnections) || other.closeConnections == closeConnections)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.isAnimateToPage, isAnimateToPage) || other.isAnimateToPage == isAnimateToPage)&&(identical(other.autoCheckUpdate, autoCheckUpdate) || other.autoCheckUpdate == autoCheckUpdate)&&(identical(other.showLabel, showLabel) || other.showLabel == showLabel)&&(identical(other.disclaimerAccepted, disclaimerAccepted) || other.disclaimerAccepted == disclaimerAccepted)&&(identical(other.crashlyticsTip, crashlyticsTip) || other.crashlyticsTip == crashlyticsTip)&&(identical(other.crashlytics, crashlytics) || other.crashlytics == crashlytics)&&(identical(other.minimizeOnExit, minimizeOnExit) || other.minimizeOnExit == minimizeOnExit)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.developerMode, developerMode) || other.developerMode == developerMode)&&(identical(other.restoreStrategy, restoreStrategy) || other.restoreStrategy == restoreStrategy)&&(identical(other.showTrayTitle, showTrayTitle) || other.showTrayTitle == showTrayTitle));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettingProps&&(identical(other.locale, locale) || other.locale == locale)&&const DeepCollectionEquality().equals(other._dashboardWidgets, _dashboardWidgets)&&(identical(other.onlyStatisticsProxy, onlyStatisticsProxy) || other.onlyStatisticsProxy == onlyStatisticsProxy)&&(identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch)&&(identical(other.silentLaunch, silentLaunch) || other.silentLaunch == silentLaunch)&&(identical(other.autoRun, autoRun) || other.autoRun == autoRun)&&(identical(other.openLogs, openLogs) || other.openLogs == openLogs)&&(identical(other.closeConnections, closeConnections) || other.closeConnections == closeConnections)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.isAnimateToPage, isAnimateToPage) || other.isAnimateToPage == isAnimateToPage)&&(identical(other.autoCheckUpdate, autoCheckUpdate) || other.autoCheckUpdate == autoCheckUpdate)&&(identical(other.showLabel, showLabel) || other.showLabel == showLabel)&&(identical(other.disclaimerAccepted, disclaimerAccepted) || other.disclaimerAccepted == disclaimerAccepted)&&(identical(other.crashlyticsTip, crashlyticsTip) || other.crashlyticsTip == crashlyticsTip)&&(identical(other.crashlytics, crashlytics) || other.crashlytics == crashlytics)&&(identical(other.minimizeOnExit, minimizeOnExit) || other.minimizeOnExit == minimizeOnExit)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.developerMode, developerMode) || other.developerMode == developerMode)&&(identical(other.restoreStrategy, restoreStrategy) || other.restoreStrategy == restoreStrategy)&&(identical(other.showTrayTitle, showTrayTitle) || other.showTrayTitle == showTrayTitle)&&(identical(other.customUserAgent, customUserAgent) || other.customUserAgent == customUserAgent));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hashAll([runtimeType,locale,const DeepCollectionEquality().hash(_dashboardWidgets),onlyStatisticsProxy,autoLaunch,silentLaunch,autoRun,openLogs,closeConnections,testUrl,isAnimateToPage,autoCheckUpdate,showLabel,disclaimerAccepted,crashlyticsTip,crashlytics,minimizeOnExit,hidden,developerMode,restoreStrategy,showTrayTitle]);
int get hashCode => Object.hashAll([runtimeType,locale,const DeepCollectionEquality().hash(_dashboardWidgets),onlyStatisticsProxy,autoLaunch,silentLaunch,autoRun,openLogs,closeConnections,testUrl,isAnimateToPage,autoCheckUpdate,showLabel,disclaimerAccepted,crashlyticsTip,crashlytics,minimizeOnExit,hidden,developerMode,restoreStrategy,showTrayTitle,customUserAgent]);
@override
String toString() {
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, crashlyticsTip: $crashlyticsTip, crashlytics: $crashlytics, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode, restoreStrategy: $restoreStrategy, showTrayTitle: $showTrayTitle)';
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, crashlyticsTip: $crashlyticsTip, crashlytics: $crashlytics, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode, restoreStrategy: $restoreStrategy, showTrayTitle: $showTrayTitle, customUserAgent: $customUserAgent)';
}
@@ -291,7 +293,7 @@ abstract mixin class _$AppSettingPropsCopyWith<$Res> implements $AppSettingProps
factory _$AppSettingPropsCopyWith(_AppSettingProps value, $Res Function(_AppSettingProps) _then) = __$AppSettingPropsCopyWithImpl;
@override @useResult
$Res call({
String? locale,@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle
String? locale,@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool crashlyticsTip, bool crashlytics, bool minimizeOnExit, bool hidden, bool developerMode, RestoreStrategy restoreStrategy, bool showTrayTitle, String customUserAgent
});
@@ -308,7 +310,7 @@ class __$AppSettingPropsCopyWithImpl<$Res>
/// Create a copy of AppSettingProps
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? locale = freezed,Object? dashboardWidgets = null,Object? onlyStatisticsProxy = null,Object? autoLaunch = null,Object? silentLaunch = null,Object? autoRun = null,Object? openLogs = null,Object? closeConnections = null,Object? testUrl = null,Object? isAnimateToPage = null,Object? autoCheckUpdate = null,Object? showLabel = null,Object? disclaimerAccepted = null,Object? crashlyticsTip = null,Object? crashlytics = null,Object? minimizeOnExit = null,Object? hidden = null,Object? developerMode = null,Object? restoreStrategy = null,Object? showTrayTitle = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? locale = freezed,Object? dashboardWidgets = null,Object? onlyStatisticsProxy = null,Object? autoLaunch = null,Object? silentLaunch = null,Object? autoRun = null,Object? openLogs = null,Object? closeConnections = null,Object? testUrl = null,Object? isAnimateToPage = null,Object? autoCheckUpdate = null,Object? showLabel = null,Object? disclaimerAccepted = null,Object? crashlyticsTip = null,Object? crashlytics = null,Object? minimizeOnExit = null,Object? hidden = null,Object? developerMode = null,Object? restoreStrategy = null,Object? showTrayTitle = null,Object? customUserAgent = null,}) {
return _then(_AppSettingProps(
locale: freezed == locale ? _self.locale : locale // ignore: cast_nullable_to_non_nullable
as String?,dashboardWidgets: null == dashboardWidgets ? _self._dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable
@@ -330,7 +332,8 @@ as bool,hidden: null == hidden ? _self.hidden : hidden // ignore: cast_nullable_
as bool,developerMode: null == developerMode ? _self.developerMode : developerMode // ignore: cast_nullable_to_non_nullable
as bool,restoreStrategy: null == restoreStrategy ? _self.restoreStrategy : restoreStrategy // ignore: cast_nullable_to_non_nullable
as RestoreStrategy,showTrayTitle: null == showTrayTitle ? _self.showTrayTitle : showTrayTitle // ignore: cast_nullable_to_non_nullable
as bool,
as bool,customUserAgent: null == customUserAgent ? _self.customUserAgent : customUserAgent // ignore: cast_nullable_to_non_nullable
as String,
));
}
+2
View File
@@ -35,6 +35,7 @@ _AppSettingProps _$AppSettingPropsFromJson(Map<String, dynamic> json) =>
) ??
RestoreStrategy.compatible,
showTrayTitle: json['showTrayTitle'] as bool? ?? true,
customUserAgent: json['customUserAgent'] as String? ?? '',
);
Map<String, dynamic> _$AppSettingPropsToJson(_AppSettingProps instance) =>
@@ -61,6 +62,7 @@ Map<String, dynamic> _$AppSettingPropsToJson(_AppSettingProps instance) =>
'developerMode': instance.developerMode,
'restoreStrategy': _$RestoreStrategyEnumMap[instance.restoreStrategy]!,
'showTrayTitle': instance.showTrayTitle,
'customUserAgent': instance.customUserAgent,
};
const _$RestoreStrategyEnumMap = {
+26 -20
View File
@@ -287,7 +287,7 @@ as String,
/// @nodoc
mixin _$UpdateParams {
Tun get tun;@JsonKey(name: 'mixed-port') int get mixedPort;@JsonKey(name: 'allow-lan') bool get allowLan;@JsonKey(name: 'find-process-mode') FindProcessMode get findProcessMode; Mode get mode;@JsonKey(name: 'log-level') LogLevel get logLevel; bool get ipv6;@JsonKey(name: 'tcp-concurrent') bool get tcpConcurrent;@JsonKey(name: 'external-controller') ExternalControllerStatus get externalController;@JsonKey(name: 'unified-delay') bool get unifiedDelay;
Tun get tun;@JsonKey(name: 'mixed-port') int get mixedPort;@JsonKey(name: 'allow-lan') bool get allowLan;@JsonKey(name: 'find-process-mode') FindProcessMode get findProcessMode; Mode get mode;@JsonKey(name: 'log-level') LogLevel get logLevel; bool get ipv6;@JsonKey(name: 'tcp-concurrent') bool get tcpConcurrent;@JsonKey(name: 'external-controller') ExternalControllerStatus get externalController;@JsonKey(name: 'unified-delay') bool get unifiedDelay;@JsonKey(name: 'geo-auto-update') bool get geoAutoUpdate;@JsonKey(name: 'geo-update-interval') int get geoUpdateInterval;
/// Create a copy of UpdateParams
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -300,16 +300,16 @@ $UpdateParamsCopyWith<UpdateParams> get copyWith => _$UpdateParamsCopyWithImpl<U
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is UpdateParams&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay));
return identical(this, other) || (other.runtimeType == runtimeType&&other is UpdateParams&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay)&&(identical(other.geoAutoUpdate, geoAutoUpdate) || other.geoAutoUpdate == geoAutoUpdate)&&(identical(other.geoUpdateInterval, geoUpdateInterval) || other.geoUpdateInterval == geoUpdateInterval));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,tun,mixedPort,allowLan,findProcessMode,mode,logLevel,ipv6,tcpConcurrent,externalController,unifiedDelay);
int get hashCode => Object.hash(runtimeType,tun,mixedPort,allowLan,findProcessMode,mode,logLevel,ipv6,tcpConcurrent,externalController,unifiedDelay,geoAutoUpdate,geoUpdateInterval);
@override
String toString() {
return 'UpdateParams(tun: $tun, mixedPort: $mixedPort, allowLan: $allowLan, findProcessMode: $findProcessMode, mode: $mode, logLevel: $logLevel, ipv6: $ipv6, tcpConcurrent: $tcpConcurrent, externalController: $externalController, unifiedDelay: $unifiedDelay)';
return 'UpdateParams(tun: $tun, mixedPort: $mixedPort, allowLan: $allowLan, findProcessMode: $findProcessMode, mode: $mode, logLevel: $logLevel, ipv6: $ipv6, tcpConcurrent: $tcpConcurrent, externalController: $externalController, unifiedDelay: $unifiedDelay, geoAutoUpdate: $geoAutoUpdate, geoUpdateInterval: $geoUpdateInterval)';
}
@@ -320,7 +320,7 @@ abstract mixin class $UpdateParamsCopyWith<$Res> {
factory $UpdateParamsCopyWith(UpdateParams value, $Res Function(UpdateParams) _then) = _$UpdateParamsCopyWithImpl;
@useResult
$Res call({
Tun tun,@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController,@JsonKey(name: 'unified-delay') bool unifiedDelay
Tun tun,@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController,@JsonKey(name: 'unified-delay') bool unifiedDelay,@JsonKey(name: 'geo-auto-update') bool geoAutoUpdate,@JsonKey(name: 'geo-update-interval') int geoUpdateInterval
});
@@ -337,7 +337,7 @@ class _$UpdateParamsCopyWithImpl<$Res>
/// Create a copy of UpdateParams
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? tun = null,Object? mixedPort = null,Object? allowLan = null,Object? findProcessMode = null,Object? mode = null,Object? logLevel = null,Object? ipv6 = null,Object? tcpConcurrent = null,Object? externalController = null,Object? unifiedDelay = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? tun = null,Object? mixedPort = null,Object? allowLan = null,Object? findProcessMode = null,Object? mode = null,Object? logLevel = null,Object? ipv6 = null,Object? tcpConcurrent = null,Object? externalController = null,Object? unifiedDelay = null,Object? geoAutoUpdate = null,Object? geoUpdateInterval = null,}) {
return _then(_self.copyWith(
tun: null == tun ? _self.tun : tun // ignore: cast_nullable_to_non_nullable
as Tun,mixedPort: null == mixedPort ? _self.mixedPort : mixedPort // ignore: cast_nullable_to_non_nullable
@@ -349,7 +349,9 @@ as LogLevel,ipv6: null == ipv6 ? _self.ipv6 : ipv6 // ignore: cast_nullable_to_n
as bool,tcpConcurrent: null == tcpConcurrent ? _self.tcpConcurrent : tcpConcurrent // ignore: cast_nullable_to_non_nullable
as bool,externalController: null == externalController ? _self.externalController : externalController // ignore: cast_nullable_to_non_nullable
as ExternalControllerStatus,unifiedDelay: null == unifiedDelay ? _self.unifiedDelay : unifiedDelay // ignore: cast_nullable_to_non_nullable
as bool,
as bool,geoAutoUpdate: null == geoAutoUpdate ? _self.geoAutoUpdate : geoAutoUpdate // ignore: cast_nullable_to_non_nullable
as bool,geoUpdateInterval: null == geoUpdateInterval ? _self.geoUpdateInterval : geoUpdateInterval // ignore: cast_nullable_to_non_nullable
as int,
));
}
/// Create a copy of UpdateParams
@@ -443,10 +445,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Tun tun, @JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay') bool unifiedDelay)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Tun tun, @JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'geo-auto-update') bool geoAutoUpdate, @JsonKey(name: 'geo-update-interval') int geoUpdateInterval)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _UpdateParams() when $default != null:
return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay);case _:
return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay,_that.geoAutoUpdate,_that.geoUpdateInterval);case _:
return orElse();
}
@@ -464,10 +466,10 @@ return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Tun tun, @JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay') bool unifiedDelay) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Tun tun, @JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'geo-auto-update') bool geoAutoUpdate, @JsonKey(name: 'geo-update-interval') int geoUpdateInterval) $default,) {final _that = this;
switch (_that) {
case _UpdateParams():
return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay);case _:
return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay,_that.geoAutoUpdate,_that.geoUpdateInterval);case _:
throw StateError('Unexpected subclass');
}
@@ -484,10 +486,10 @@ return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Tun tun, @JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay') bool unifiedDelay)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Tun tun, @JsonKey(name: 'mixed-port') int mixedPort, @JsonKey(name: 'allow-lan') bool allowLan, @JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode, @JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6, @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent, @JsonKey(name: 'external-controller') ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay') bool unifiedDelay, @JsonKey(name: 'geo-auto-update') bool geoAutoUpdate, @JsonKey(name: 'geo-update-interval') int geoUpdateInterval)? $default,) {final _that = this;
switch (_that) {
case _UpdateParams() when $default != null:
return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay);case _:
return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay,_that.geoAutoUpdate,_that.geoUpdateInterval);case _:
return null;
}
@@ -499,7 +501,7 @@ return $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_
@JsonSerializable()
class _UpdateParams implements UpdateParams {
const _UpdateParams({required this.tun, @JsonKey(name: 'mixed-port') required this.mixedPort, @JsonKey(name: 'allow-lan') required this.allowLan, @JsonKey(name: 'find-process-mode') required this.findProcessMode, required this.mode, @JsonKey(name: 'log-level') required this.logLevel, required this.ipv6, @JsonKey(name: 'tcp-concurrent') required this.tcpConcurrent, @JsonKey(name: 'external-controller') required this.externalController, @JsonKey(name: 'unified-delay') required this.unifiedDelay});
const _UpdateParams({required this.tun, @JsonKey(name: 'mixed-port') required this.mixedPort, @JsonKey(name: 'allow-lan') required this.allowLan, @JsonKey(name: 'find-process-mode') required this.findProcessMode, required this.mode, @JsonKey(name: 'log-level') required this.logLevel, required this.ipv6, @JsonKey(name: 'tcp-concurrent') required this.tcpConcurrent, @JsonKey(name: 'external-controller') required this.externalController, @JsonKey(name: 'unified-delay') required this.unifiedDelay, @JsonKey(name: 'geo-auto-update') this.geoAutoUpdate = false, @JsonKey(name: 'geo-update-interval') this.geoUpdateInterval = 24});
factory _UpdateParams.fromJson(Map<String, dynamic> json) => _$UpdateParamsFromJson(json);
@override final Tun tun;
@@ -512,6 +514,8 @@ class _UpdateParams implements UpdateParams {
@override@JsonKey(name: 'tcp-concurrent') final bool tcpConcurrent;
@override@JsonKey(name: 'external-controller') final ExternalControllerStatus externalController;
@override@JsonKey(name: 'unified-delay') final bool unifiedDelay;
@override@JsonKey(name: 'geo-auto-update') final bool geoAutoUpdate;
@override@JsonKey(name: 'geo-update-interval') final int geoUpdateInterval;
/// Create a copy of UpdateParams
/// with the given fields replaced by the non-null parameter values.
@@ -526,16 +530,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _UpdateParams&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _UpdateParams&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay)&&(identical(other.geoAutoUpdate, geoAutoUpdate) || other.geoAutoUpdate == geoAutoUpdate)&&(identical(other.geoUpdateInterval, geoUpdateInterval) || other.geoUpdateInterval == geoUpdateInterval));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,tun,mixedPort,allowLan,findProcessMode,mode,logLevel,ipv6,tcpConcurrent,externalController,unifiedDelay);
int get hashCode => Object.hash(runtimeType,tun,mixedPort,allowLan,findProcessMode,mode,logLevel,ipv6,tcpConcurrent,externalController,unifiedDelay,geoAutoUpdate,geoUpdateInterval);
@override
String toString() {
return 'UpdateParams(tun: $tun, mixedPort: $mixedPort, allowLan: $allowLan, findProcessMode: $findProcessMode, mode: $mode, logLevel: $logLevel, ipv6: $ipv6, tcpConcurrent: $tcpConcurrent, externalController: $externalController, unifiedDelay: $unifiedDelay)';
return 'UpdateParams(tun: $tun, mixedPort: $mixedPort, allowLan: $allowLan, findProcessMode: $findProcessMode, mode: $mode, logLevel: $logLevel, ipv6: $ipv6, tcpConcurrent: $tcpConcurrent, externalController: $externalController, unifiedDelay: $unifiedDelay, geoAutoUpdate: $geoAutoUpdate, geoUpdateInterval: $geoUpdateInterval)';
}
@@ -546,7 +550,7 @@ abstract mixin class _$UpdateParamsCopyWith<$Res> implements $UpdateParamsCopyWi
factory _$UpdateParamsCopyWith(_UpdateParams value, $Res Function(_UpdateParams) _then) = __$UpdateParamsCopyWithImpl;
@override @useResult
$Res call({
Tun tun,@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController,@JsonKey(name: 'unified-delay') bool unifiedDelay
Tun tun,@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController,@JsonKey(name: 'unified-delay') bool unifiedDelay,@JsonKey(name: 'geo-auto-update') bool geoAutoUpdate,@JsonKey(name: 'geo-update-interval') int geoUpdateInterval
});
@@ -563,7 +567,7 @@ class __$UpdateParamsCopyWithImpl<$Res>
/// Create a copy of UpdateParams
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? tun = null,Object? mixedPort = null,Object? allowLan = null,Object? findProcessMode = null,Object? mode = null,Object? logLevel = null,Object? ipv6 = null,Object? tcpConcurrent = null,Object? externalController = null,Object? unifiedDelay = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? tun = null,Object? mixedPort = null,Object? allowLan = null,Object? findProcessMode = null,Object? mode = null,Object? logLevel = null,Object? ipv6 = null,Object? tcpConcurrent = null,Object? externalController = null,Object? unifiedDelay = null,Object? geoAutoUpdate = null,Object? geoUpdateInterval = null,}) {
return _then(_UpdateParams(
tun: null == tun ? _self.tun : tun // ignore: cast_nullable_to_non_nullable
as Tun,mixedPort: null == mixedPort ? _self.mixedPort : mixedPort // ignore: cast_nullable_to_non_nullable
@@ -575,7 +579,9 @@ as LogLevel,ipv6: null == ipv6 ? _self.ipv6 : ipv6 // ignore: cast_nullable_to_n
as bool,tcpConcurrent: null == tcpConcurrent ? _self.tcpConcurrent : tcpConcurrent // ignore: cast_nullable_to_non_nullable
as bool,externalController: null == externalController ? _self.externalController : externalController // ignore: cast_nullable_to_non_nullable
as ExternalControllerStatus,unifiedDelay: null == unifiedDelay ? _self.unifiedDelay : unifiedDelay // ignore: cast_nullable_to_non_nullable
as bool,
as bool,geoAutoUpdate: null == geoAutoUpdate ? _self.geoAutoUpdate : geoAutoUpdate // ignore: cast_nullable_to_non_nullable
as bool,geoUpdateInterval: null == geoUpdateInterval ? _self.geoUpdateInterval : geoUpdateInterval // ignore: cast_nullable_to_non_nullable
as int,
));
}
+5
View File
@@ -35,6 +35,8 @@ _UpdateParams _$UpdateParamsFromJson(Map<String, dynamic> json) =>
json['external-controller'],
),
unifiedDelay: json['unified-delay'] as bool,
geoAutoUpdate: json['geo-auto-update'] as bool? ?? false,
geoUpdateInterval: (json['geo-update-interval'] as num?)?.toInt() ?? 24,
);
Map<String, dynamic> _$UpdateParamsToJson(_UpdateParams instance) =>
@@ -50,6 +52,8 @@ Map<String, dynamic> _$UpdateParamsToJson(_UpdateParams instance) =>
'external-controller':
_$ExternalControllerStatusEnumMap[instance.externalController]!,
'unified-delay': instance.unifiedDelay,
'geo-auto-update': instance.geoAutoUpdate,
'geo-update-interval': instance.geoUpdateInterval,
};
const _$FindProcessModeEnumMap = {
@@ -164,6 +168,7 @@ const _$CoreEventTypeEnumMap = {
CoreEventType.request: 'request',
CoreEventType.loaded: 'loaded',
CoreEventType.crash: 'crash',
CoreEventType.geoUpdate: 'geoUpdate',
};
_InvokeMessage _$InvokeMessageFromJson(Map<String, dynamic> json) =>
+8 -2
View File
@@ -139,7 +139,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
if (file == null) {
return;
}
final res = utf8.decode(file.bytes?.toList() ?? []);
final res = utf8.decode(await file.readBytes());
_controller.text = res;
}
@@ -150,6 +150,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
title: appLocalizations.import,
value: '',
labelText: appLocalizations.url,
inputFormatters: TextInputLimits.limit(TextInputLimits.url),
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip(appLocalizations.value);
@@ -442,7 +443,11 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [bar, const SizedBox(height: 12), _buildFindInput(context, value)],
children: [
bar,
const SizedBox(height: 12),
_buildFindInput(context, value),
],
);
}
return bar;
@@ -499,6 +504,7 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
child: TextField(
maxLines: 1,
focusNode: focusNode,
inputFormatters: TextInputLimits.limit(TextInputLimits.search),
style: context.textTheme.bodyMedium,
decoration: const InputDecoration(
border: OutlineInputBorder(),
+19 -29
View File
@@ -6,7 +6,6 @@ import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
@@ -30,9 +29,6 @@ class HomePage extends StatelessWidget {
child: Consumer(
builder: (context, ref, child) {
final state = ref.watch(navigationStateProvider);
final systemUiOverlayStyle = ref.read(
systemUiOverlayStyleStateProvider,
);
final isMobile = state.viewMode == ViewMode.mobile;
final navigationItems = state.navigationItems;
final currentIndex = state.currentIndex;
@@ -54,34 +50,28 @@ class HomePage extends StatelessWidget {
),
);
if (isMobile) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: systemUiOverlayStyle.copyWith(
systemNavigationBarColor:
context.colorScheme.surfaceContainer,
),
child: Column(
children: [
Flexible(
flex: 1,
child: MediaQuery.removePadding(
removeTop: false,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: child!,
),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: false,
return Column(
children: [
Flexible(
flex: 1,
child: MediaQuery.removePadding(
removeTop: false,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: bottomNavigationBar,
child: child!,
),
],
),
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: false,
removeLeft: true,
removeRight: true,
context: context,
child: bottomNavigationBar,
),
],
);
} else {
return child!;
@@ -287,7 +277,7 @@ class HomeBackScopeContainer extends ConsumerWidget {
} else {
await globalState.container
.read(systemActionProvider.notifier)
.handleBackOrExit();
.handleClose();
}
return false;
},
+4 -1
View File
@@ -61,7 +61,10 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
final double sideLength = min(400, MediaQuery.of(context).size.width * 0.67);
final double sideLength = min(
400,
MediaQuery.of(context).size.width * 0.67,
);
final scanWindow = Rect.fromCenter(
center: MediaQuery.sizeOf(context).center(Offset.zero),
width: sideLength,
+2 -6
View File
@@ -53,9 +53,7 @@ class App {
}
Future<bool?> requestNotificationsPermission() async {
return methodChannel.invokeMethod<bool>(
'requestNotificationsPermission',
);
return methodChannel.invokeMethod<bool>('requestNotificationsPermission');
}
Future<bool> openFile(String path) async {
@@ -74,9 +72,7 @@ class App {
}
Future<bool?> tip(String? message) async {
return methodChannel.invokeMethod<bool>('tip', {
'message': '$message',
});
return methodChannel.invokeMethod<bool>('tip', {'message': '$message'});
}
Future<bool?> initShortcuts() async {
+28 -7
View File
@@ -224,6 +224,7 @@ class SetupAction extends _$SetupAction {
final message = await coreController.updateConfig(
updateParams.copyWith.tun(enable: realTunEnable),
);
ref.read(checkIpNumProvider.notifier).add();
if (message.isNotEmpty) throw message;
});
});
@@ -254,7 +255,6 @@ class SetupAction extends _$SetupAction {
.read(proxiesActionProvider.notifier)
.updateCurrentGroupName(GroupName.GLOBAL.name);
}
ref.read(checkIpNumProvider.notifier).add();
}
void autoApplyProfile() {
@@ -593,9 +593,11 @@ class SystemAction extends _$SystemAction {
}
}
Future<void> handleBackOrExit() async {
if (ref.read(backBlockProvider)) return;
if (ref.read(appSettingProvider).minimizeOnExit) {
Future<void> handleClose([bool exit = true]) async {
if (!system.isDesktop) {
if (ref.read(backBlockProvider)) return;
}
if (ref.read(appSettingProvider).minimizeOnExit || !exit) {
if (system.isDesktop) {
await preferences.saveConfig(ref.read(configProvider));
}
@@ -910,14 +912,14 @@ class ProfilesAction extends _$ProfilesAction {
Future<void> addProfileFormFile() async {
final platformFile = await globalState.safeRun(picker.pickerFile);
final bytes = platformFile?.bytes;
if (bytes == null) return;
if (platformFile == null) return;
final bytes = await platformFile.readBytes();
globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
ref.read(currentPageLabelProvider.notifier).toProfiles();
final profile = await globalState.loadingRun(
tag: LoadingTag.profiles,
() async {
return Profile.normal(label: platformFile?.name).saveFile(bytes);
return Profile.normal(label: platformFile.name).saveFile(bytes);
},
title: currentAppLocalizations.addProfile,
);
@@ -973,3 +975,22 @@ class ProfilesAction extends _$ProfilesAction {
await coreController.deleteFile(providersDirPath);
}
}
@Riverpod(keepAlive: true)
class GeoResourceAction extends _$GeoResourceAction {
@override
void build() {}
Future<void> updateGeoResource(GeoResource geoResource) async {
await coreController.updateGeoData(geoResource.name);
}
void updateGeoResourceUrl(GeoResource geoResource, String newUrl) {
if (!newUrl.isUrl) {
throw 'Invalid url';
}
ref.read(patchClashConfigProvider.notifier).update((state) {
return state.copyWith(geoXUrl: {...state.geoXUrl, geoResource: newUrl});
});
}
}

Some files were not shown because too many files have changed in this diff Show More