Files
FlClash/lib/providers/actions/system.dart
T

167 lines
4.4 KiB
Dart
Raw Normal View History

2026-08-05 10:55:33 +08:00
part of '../action.dart';
@Riverpod(keepAlive: true)
class SystemAction extends _$SystemAction {
CoreController get _core => ref.read(coreHandlerProvider);
2026-08-05 10:55:33 +08:00
SystemExitCoordinator? _exitCoordinator;
@override
void build() {}
Future<List<Package>> getPackages() async {
ref.read(packagesProvider.notifier).value = await app?.getPackages() ?? [];
2026-08-05 10:55:33 +08:00
return ref.read(packagesProvider);
}
Future<bool> isInstalledAppsPermissionGranted() async {
return await app?.isInstalledAppsPermissionGranted() ?? true;
}
Future<bool> requestInstalledAppsPermission() async {
return await app?.requestInstalledAppsPermission() ?? false;
}
Future<void> handleExit([bool needSave = true]) {
2026-08-05 10:55:33 +08:00
final coordinator = _exitCoordinator ??= SystemExitCoordinator(
watchdogDuration: exitWatchdogDuration,
closeWindow: closeWindow,
closeCore: closeCore,
exitApplication: exitApplication,
);
return coordinator.exit(cleanup: () => cleanupExitResources(needSave));
}
@protected
Duration get exitWatchdogDuration => const Duration(seconds: 3);
@protected
Future<void> cleanupExitResources(bool needSave) async {
final saveOperation = needSave ? _savePreferencesSafely() : null;
final tray = trayPort;
if (tray != null) {
await tray.shutdown().onError<Object>((error, stackTrace) {
commonPrint.log(
'Tray shutdown failed: ${compactError(error)}',
logLevel: LogLevel.error,
);
});
}
2026-08-05 10:55:33 +08:00
await Future.wait([
?saveOperation,
bootGuard.markClosed(),
if (systemDnsCoordinator != null) systemDnsCoordinator!.shutdown(),
?systemProxyQueue?.close(),
2026-08-05 10:55:33 +08:00
]);
}
Future<void> _savePreferencesSafely() async {
try {
await savePreferences();
} catch (error) {
commonPrint.log(
'Preferences save failed: ${compactError(error)}',
logLevel: LogLevel.error,
);
}
}
@protected
Future<void> savePreferences() async {
final port = windowPort;
if (port != null) {
try {
final current = ref.read(windowSettingProvider);
final geometry = await port.captureNormalGeometry(current);
if (geometry != null) {
ref.read(windowSettingProvider.notifier).value = geometry;
}
} catch (error) {
commonPrint.log(
'Window geometry capture failed: ${compactError(error)}',
logLevel: LogLevel.warning,
);
}
}
await preferences.saveConfig(ref.read(configProvider));
}
2026-08-05 10:55:33 +08:00
@protected
Future<void> closeWindow() async {
await windowPort?.close();
2026-08-05 10:55:33 +08:00
}
@protected
Future<void> closeCore() async {
await _core.close();
2026-08-05 10:55:33 +08:00
commonPrint.log('exit');
}
@protected
Future<void> exitApplication() async {
await system.exit();
windowPort?.forceExit();
2026-08-05 10:55:33 +08:00
}
Future<void> handleClose([bool exit = true]) async {
if (ref.read(appSettingProvider).minimizeOnExit || !exit) {
if (system.isDesktop) {
await _savePreferencesSafely();
2026-08-05 10:55:33 +08:00
}
await system.back();
await windowPort?.hide();
2026-08-05 10:55:33 +08:00
} else {
await handleExit();
}
}
Future<void> updateVisible() async {
await windowPort?.toggle();
2026-08-05 10:55:33 +08:00
}
void updateTun() {
ref
.read(patchClashConfigProvider.notifier)
.update((state) => state.copyWith.tun(enable: !state.tun.enable));
}
void updateSystemProxy() {
ref
.read(networkSettingProvider.notifier)
.update((state) => state.copyWith(systemProxy: !state.systemProxy));
}
Future<void> copyProxyEnv() async {
final port = ref.read(
patchClashConfigProvider.select((state) => state.mixedPort),
);
await Clipboard.setData(
ClipboardData(text: proxyEnvCommand(port, isWindows: system.isWindows)),
);
}
2026-08-05 10:55:33 +08:00
void updateAutoLaunch() {
ref
.read(appSettingProvider.notifier)
.update((state) => state.copyWith(autoLaunch: !state.autoLaunch));
}
Future<void> updateTray() async {
await trayPort?.update(
2026-08-05 10:55:33 +08:00
trayState: ref.read(trayStateProvider),
traffic: ref.read(
trafficsProvider.select(
(state) => state.list.safeLast(const Traffic()),
),
),
read: globalState.container.read,
2026-08-05 10:55:33 +08:00
);
}
Future<void> updateLocalIp() async {
ref.read(localIpProvider.notifier).value = null;
await Future.delayed(commonDuration);
ref.read(localIpProvider.notifier).value = await getLocalIpAddress();
2026-08-05 10:55:33 +08:00
}
}