Compare commits
1 Commits
v0.8.90-pr
...
v0.8.89-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4ddb287d5 |
6
.github/workflows/build.yaml
vendored
6
.github/workflows/build.yaml
vendored
@@ -27,9 +27,9 @@ jobs:
|
||||
- platform: macos
|
||||
os: macos-latest
|
||||
arch: arm64
|
||||
# - platform: windows
|
||||
# os: windows-11-arm
|
||||
# arch: arm64
|
||||
- platform: windows
|
||||
os: windows-11-arm
|
||||
arch: arm64
|
||||
- platform: linux
|
||||
os: ubuntu-24.04-arm
|
||||
arch: arm64
|
||||
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -45,19 +45,11 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
/android/**/.cxx
|
||||
/android/**/build
|
||||
/android/common/**/.**/
|
||||
/android/common/local.*
|
||||
/android/core/**/includes/
|
||||
/android/core/**/cmake-build-*/
|
||||
/android/core/**/jniLibs/
|
||||
|
||||
|
||||
#FlClash
|
||||
|
||||
#libclash
|
||||
/libclash/
|
||||
/android/app/src/main/jniLibs/
|
||||
/services/helper/target
|
||||
/macos/**/Package.resolved
|
||||
devtools_options.yaml
|
||||
|
||||
#jniLibs
|
||||
/android/app/src/main/jniLibs/
|
||||
|
||||
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,13 +1,3 @@
|
||||
## v0.8.89
|
||||
|
||||
- Fix some issues
|
||||
|
||||
- Optimize Windows service mode
|
||||
|
||||
- Update core
|
||||
|
||||
- Update changelog
|
||||
|
||||
## v0.8.88
|
||||
|
||||
- Add android separates the core process
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
|
||||
<activity
|
||||
android:name=".TempActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@style/TransparentTheme">
|
||||
<intent-filter>
|
||||
|
||||
@@ -92,7 +92,6 @@ object State {
|
||||
|
||||
suspend fun destroyServiceEngine() {
|
||||
runLock.withLock {
|
||||
GlobalState.log("Destroy service engine")
|
||||
withContext(Dispatchers.Main) {
|
||||
runCatching {
|
||||
serviceFlutterEngine?.destroy()
|
||||
@@ -104,12 +103,10 @@ object State {
|
||||
|
||||
suspend fun startServiceWithEngine() {
|
||||
runLock.withLock {
|
||||
if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.START) {
|
||||
if (serviceFlutterEngine != null || runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.START) {
|
||||
return
|
||||
}
|
||||
GlobalState.log("Create service engine")
|
||||
withContext(Dispatchers.Main) {
|
||||
serviceFlutterEngine?.destroy()
|
||||
serviceFlutterEngine = FlutterEngine(GlobalState.application)
|
||||
serviceFlutterEngine?.plugins?.add(ServicePlugin())
|
||||
serviceFlutterEngine?.plugins?.add(AppPlugin())
|
||||
|
||||
@@ -30,6 +30,6 @@ class TempActivity : Activity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
finish()
|
||||
finishAndRemoveTask()
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,6 @@ var (
|
||||
)
|
||||
|
||||
func handleInitClash(paramsString string) bool {
|
||||
runLock.Lock()
|
||||
defer runLock.Unlock()
|
||||
var params = InitParams{}
|
||||
err := json.Unmarshal([]byte(paramsString), ¶ms)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
|
||||
class Debouncer {
|
||||
@@ -69,7 +68,7 @@ Future<T> retry<T>({
|
||||
required Future<T> Function() task,
|
||||
int maxAttempts = 3,
|
||||
required bool Function(T res) retryIf,
|
||||
Duration delay = midDuration,
|
||||
Duration delay = Duration.zero,
|
||||
}) async {
|
||||
int attempts = 0;
|
||||
while (attempts < maxAttempts) {
|
||||
@@ -79,7 +78,7 @@ Future<T> retry<T>({
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
throw 'retry error';
|
||||
throw 'unknown error';
|
||||
}
|
||||
|
||||
final debouncer = Debouncer();
|
||||
|
||||
@@ -186,26 +186,26 @@ class Windows {
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
|
||||
if (result <= 32) {
|
||||
if (result < 42) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Future<void> _killProcess(int port) async {
|
||||
// final result = await Process.run('netstat', ['-ano']);
|
||||
// final lines = result.stdout.toString().trim().split('\n');
|
||||
// for (final line in lines) {
|
||||
// if (!line.contains(':$port') || !line.contains('LISTENING')) {
|
||||
// continue;
|
||||
// }
|
||||
// final parts = line.trim().split(RegExp(r'\s+'));
|
||||
// final pid = int.tryParse(parts.last);
|
||||
// if (pid != null) {
|
||||
// await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
Future<void> _killProcess(int port) async {
|
||||
final result = await Process.run('netstat', ['-ano']);
|
||||
final lines = result.stdout.toString().trim().split('\n');
|
||||
for (final line in lines) {
|
||||
if (!line.contains(':$port') || !line.contains('LISTENING')) {
|
||||
continue;
|
||||
}
|
||||
final parts = line.trim().split(RegExp(r'\s+'));
|
||||
final pid = int.tryParse(parts.last);
|
||||
if (pid != null) {
|
||||
await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<WindowsHelperServiceStatus> checkService() async {
|
||||
// final qcResult = await Process.run('sc', ['qc', appHelperService]);
|
||||
@@ -231,18 +231,16 @@ class Windows {
|
||||
return true;
|
||||
}
|
||||
|
||||
await _killProcess(helperPort);
|
||||
|
||||
final command = [
|
||||
'/c',
|
||||
if (status == WindowsHelperServiceStatus.presence) ...[
|
||||
'taskkill',
|
||||
'/F',
|
||||
'/IM',
|
||||
'$appHelperService.exe'
|
||||
' & '
|
||||
'sc',
|
||||
'sc',
|
||||
'delete',
|
||||
appHelperService,
|
||||
'&',
|
||||
'/force',
|
||||
'&&',
|
||||
],
|
||||
'sc',
|
||||
'create',
|
||||
@@ -258,12 +256,8 @@ class Windows {
|
||||
final res = runas('cmd.exe', command);
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
final retryStatus = await retry(
|
||||
task: checkService,
|
||||
retryIf: (status) => status == WindowsHelperServiceStatus.running,
|
||||
delay: commonDuration,
|
||||
);
|
||||
return res && retryStatus == WindowsHelperServiceStatus.running;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<bool> registerTask(String appName) async {
|
||||
|
||||
@@ -322,15 +322,12 @@ class Utils {
|
||||
return SingleActivator(trigger, control: control, meta: !control);
|
||||
}
|
||||
|
||||
FutureOr<T> handleWatch<T>({
|
||||
required Function function,
|
||||
required void Function(T data, int elapsedMilliseconds) onWatch,
|
||||
}) async {
|
||||
FutureOr<T> handleWatch<T>(Function function) async {
|
||||
if (kDebugMode) {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final res = await function();
|
||||
stopwatch.stop();
|
||||
onWatch(res, stopwatch.elapsedMilliseconds);
|
||||
commonPrint.log('耗时:${stopwatch.elapsedMilliseconds} ms');
|
||||
return res;
|
||||
}
|
||||
return await function();
|
||||
|
||||
@@ -962,7 +962,7 @@ class AppController {
|
||||
final res = await futureFunction();
|
||||
return res;
|
||||
} catch (e) {
|
||||
commonPrint.log('$title===> $e', logLevel: LogLevel.warning);
|
||||
commonPrint.log('$futureFunction ===> $e', logLevel: LogLevel.warning);
|
||||
if (realSilence) {
|
||||
globalState.showNotifier(e.toString());
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'dart:isolate';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
mixin CoreInterface {
|
||||
Future<bool> init(InitParams params);
|
||||
@@ -87,18 +86,7 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
await connected;
|
||||
if (kDebugMode) {
|
||||
commonPrint.log('Invoke ${method.name} ${DateTime.now()} $data');
|
||||
}
|
||||
|
||||
return utils.handleWatch(
|
||||
function: () async {
|
||||
return await invoke(method: method, data: data, timeout: timeout);
|
||||
},
|
||||
onWatch: (data, elapsedMilliseconds) {
|
||||
commonPrint.log('Invoke ${method.name} ${elapsedMilliseconds}ms');
|
||||
},
|
||||
);
|
||||
return invoke(method: method, data: data, timeout: timeout);
|
||||
}
|
||||
|
||||
Future<T?> invoke<T>({
|
||||
@@ -157,7 +145,7 @@ abstract class CoreHandlerInterface with CoreInterface {
|
||||
@override
|
||||
Future<Result> getConfig(String path) async {
|
||||
return await _invoke<Result>(method: ActionMethod.getConfig, data: path) ??
|
||||
Result.success({});
|
||||
Result<Map<String, dynamic>>.success({});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -38,29 +38,23 @@ class CoreService extends CoreHandlerInterface {
|
||||
completer?.complete(data);
|
||||
}
|
||||
|
||||
Future<void> _initServer() async {
|
||||
final server = await retry(
|
||||
task: () async {
|
||||
try {
|
||||
final address = !system.isWindows
|
||||
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
|
||||
: InternetAddress(localhost, type: InternetAddressType.IPv4);
|
||||
await _deleteSocketFile();
|
||||
final server = await ServerSocket.bind(address, 0, shared: true);
|
||||
server.listen((socket) async {
|
||||
await _attachSocket(socket);
|
||||
});
|
||||
return server;
|
||||
} catch (_) {
|
||||
return null;
|
||||
void _initServer() {
|
||||
runZonedGuarded(
|
||||
() async {
|
||||
final address = !system.isWindows
|
||||
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
|
||||
: InternetAddress(localhost, type: InternetAddressType.IPv4);
|
||||
await _deleteSocketFile();
|
||||
final server = await ServerSocket.bind(address, 0, shared: true);
|
||||
_serverCompleter.complete(server);
|
||||
await for (final socket in server) {
|
||||
await _attachSocket(socket);
|
||||
}
|
||||
},
|
||||
retryIf: (server) => server == null,
|
||||
(error, stack) async {
|
||||
commonPrint.log('Service error: $error', logLevel: LogLevel.warning);
|
||||
},
|
||||
);
|
||||
if (server == null) {
|
||||
exit(0);
|
||||
}
|
||||
_serverCompleter.complete(server);
|
||||
}
|
||||
|
||||
Future<void> _attachSocket(Socket socket) async {
|
||||
|
||||
@@ -33,14 +33,15 @@ Future<void> _service(List<String> flags) async {
|
||||
},
|
||||
),
|
||||
);
|
||||
app?.tip(appLocalizations.startVpn);
|
||||
final version = await system.version;
|
||||
await coreController.init(version);
|
||||
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
|
||||
enable: false,
|
||||
);
|
||||
coreController.setupConfig(clashConfig).then((_) async {
|
||||
Future(() async {
|
||||
app?.tip(appLocalizations.startVpn);
|
||||
final version = await system.version;
|
||||
await coreController.init(version);
|
||||
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
|
||||
enable: false,
|
||||
);
|
||||
await globalState.handleStart();
|
||||
await coreController.setupConfig(clashConfig);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ extension ActionResultExt on ActionResult {
|
||||
if (code == ResultType.success) {
|
||||
return Result.success(data);
|
||||
} else {
|
||||
return Result.error('$data');
|
||||
return Result.error(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,13 +304,7 @@ class GlobalState {
|
||||
|
||||
Future<void> genConfigFile(ClashConfig pathConfig) async {
|
||||
final configFilePath = await appPath.configFilePath;
|
||||
var config = {};
|
||||
try {
|
||||
config = await patchRawConfig(patchConfig: pathConfig);
|
||||
} catch (e) {
|
||||
globalState.showNotifier(e.toString());
|
||||
config = {};
|
||||
}
|
||||
final config = await patchRawConfig(patchConfig: pathConfig);
|
||||
final res = await Isolate.run<String>(() async {
|
||||
try {
|
||||
final res = json.encode(config);
|
||||
|
||||
@@ -147,8 +147,7 @@ class ProviderItem extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 4),
|
||||
if (provider.updateAt.microsecondsSinceEpoch > 0)
|
||||
Text(_buildProviderDesc()),
|
||||
Text(_buildProviderDesc()),
|
||||
const SizedBox(height: 4),
|
||||
if (provider.subscriptionInfo != null)
|
||||
SubscriptionInfoView(subscriptionInfo: provider.subscriptionInfo),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: fl_clash
|
||||
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
|
||||
publish_to: 'none'
|
||||
version: 0.8.90+2025100101
|
||||
version: 0.8.89+2025092402
|
||||
environment:
|
||||
sdk: '>=3.8.0 <4.0.0'
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ SolidCompression=yes
|
||||
SetupIconFile={{SETUP_ICON_FILE}}
|
||||
WizardStyle=modern
|
||||
PrivilegesRequired={{PRIVILEGES_REQUIRED}}
|
||||
ArchitecturesAllowed=x64 arm64
|
||||
ArchitecturesInstallIn64BitMode=x64 arm64
|
||||
ArchitecturesAllowed={{ARCH}}
|
||||
ArchitecturesInstallIn64BitMode={{ARCH}}
|
||||
|
||||
[Code]
|
||||
procedure KillProcesses;
|
||||
|
||||
Reference in New Issue
Block a user