Files
MWClash/lib/controller.dart

389 lines
9.9 KiB
Dart
Raw Permalink Normal View History

2024-04-30 23:38:49 +08:00
import 'dart:async';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'clash/core.dart';
import 'enum/enum.dart';
import 'models/models.dart';
import 'common/common.dart';
class AppController {
final BuildContext context;
late AppState appState;
late Config config;
late ClashConfig clashConfig;
late Measure measure;
late Function updateClashConfigDebounce;
AppController(this.context) {
appState = context.read<AppState>();
config = context.read<Config>();
clashConfig = context.read<ClashConfig>();
updateClashConfigDebounce = debounce<Function()>(() async {
await updateClashConfig();
});
measure = Measure.of(context);
}
Future<void> updateSystemProxy(bool isStart) async {
if (isStart) {
await globalState.startSystemProxy(
config: config,
clashConfig: clashConfig,
);
updateRunTime();
updateTraffic();
globalState.updateFunctionLists = [
updateRunTime,
updateTraffic,
];
2024-05-17 15:39:41 +08:00
clearShowProxyDelay();
testShowProxyDelay();
2024-04-30 23:38:49 +08:00
} else {
await globalState.stopSystemProxy();
appState.traffics = [];
appState.runTime = null;
}
}
updateCoreVersionInfo() {
globalState.updateCoreVersionInfo(appState);
}
updateRunTime() {
if (proxyManager.startTime != null) {
final startTimeStamp = proxyManager.startTime!.millisecondsSinceEpoch;
2024-05-06 10:32:39 +08:00
final nowTimeStamp = DateTime.now().millisecondsSinceEpoch;
2024-04-30 23:38:49 +08:00
appState.runTime = nowTimeStamp - startTimeStamp;
} else {
appState.runTime = null;
}
}
updateTraffic() {
globalState.updateTraffic(
config: config,
appState: appState,
);
}
changeProxy() {
globalState.changeProxy(
appState: appState,
config: config,
clashConfig: clashConfig,
2024-04-30 23:38:49 +08:00
);
}
addProfile(Profile profile) {
config.setProfile(profile);
if (config.currentProfileId != null) return;
changeProfile(profile.id);
}
deleteProfile(String id) async {
config.deleteProfileById(id);
final profilePath = await appPath.getProfilePath(id);
if (profilePath == null) return;
clashCore.clearEffect(profilePath);
2024-04-30 23:38:49 +08:00
if (config.currentProfileId == id) {
if (config.profiles.isNotEmpty) {
final updateId = config.profiles.first.id;
changeProfile(updateId);
} else {
changeProfile(null);
}
}
}
updateProfile(String id) async {
final profile = config.getCurrentProfileForId(id);
if (profile != null) {
final res = await profile.update();
if (res.type == ResultType.success) {
config.setProfile(profile);
}
}
}
2024-05-05 02:40:35 +08:00
Future<String> updateClashConfig({bool isPatch = true}) async {
2024-04-30 23:38:49 +08:00
return await globalState.updateClashConfig(
clashConfig: clashConfig,
config: config,
isPatch: isPatch,
);
}
2024-05-05 02:40:35 +08:00
applyProfile() async {
await globalState.applyProfile(
2024-05-04 16:39:21 +08:00
appState: appState,
config: config,
clashConfig: clashConfig,
);
}
2024-05-06 10:32:39 +08:00
Function? _changeProfileDebounce;
2024-05-04 21:51:40 +08:00
changeProfileDebounce(String? profileId) {
if (profileId == config.currentProfileId) return;
config.currentProfileId = profileId;
_changeProfileDebounce ??= debounce<Function(String?)>((profileId) async {
2024-05-05 02:40:35 +08:00
await applyProfile();
appState.delayMap = {};
saveConfigPreferences();
2024-05-04 21:51:40 +08:00
});
_changeProfileDebounce!([profileId]);
}
2024-04-30 23:38:49 +08:00
changeProfile(String? value) async {
if (value == config.currentProfileId) return;
config.currentProfileId = value;
2024-05-04 16:39:21 +08:00
await applyProfile();
2024-04-30 23:38:49 +08:00
appState.delayMap = {};
saveConfigPreferences();
}
autoUpdateProfiles() async {
for (final profile in config.profiles) {
if (!profile.autoUpdate) return;
final isNotNeedUpdate = profile.lastUpdateDate
?.add(
2024-05-06 10:32:39 +08:00
profile.autoUpdateDuration,
)
2024-04-30 23:38:49 +08:00
.isBeforeNow();
if (isNotNeedUpdate == false) continue;
2024-05-04 16:39:21 +08:00
await profile.update();
2024-04-30 23:38:49 +08:00
}
}
2024-05-03 14:31:10 +08:00
Future<void> updateGroups() async {
await globalState.updateGroups(appState);
2024-04-30 23:38:49 +08:00
}
updateSystemColorSchemes(SystemColorSchemes systemColorSchemes) {
appState.systemColorSchemes = systemColorSchemes;
}
savePreferences() async {
await saveConfigPreferences();
await saveClashConfigPreferences();
}
saveConfigPreferences() async {
debugPrint("saveConfigPreferences");
await preferences.saveConfig(config);
}
saveClashConfigPreferences() async {
debugPrint("saveClashConfigPreferences");
await preferences.saveClashConfig(clashConfig);
}
handleBackOrExit() async {
if (config.isMinimizeOnExit) {
if (system.isDesktop) {
await savePreferences();
}
await system.back();
} else {
await handleExit();
}
}
handleExit() async {
await updateSystemProxy(false);
await savePreferences();
clashCore.shutdown();
system.exit();
}
updateLogStatus() {
if (config.openLogs) {
clashCore.startLog();
} else {
clashCore.stopLog();
appState.logs = [];
}
}
afterInit() async {
2024-05-06 10:32:39 +08:00
if (config.autoRun) {
await updateSystemProxy(true);
} else {
await proxyManager.updateStartTime();
await updateSystemProxy(proxyManager.isStart);
}
autoUpdateProfiles();
updateLogStatus();
if (!config.silentLaunch) {
window?.show();
2024-04-30 23:38:49 +08:00
}
}
2024-05-06 14:29:49 +08:00
healthcheck() {
2024-05-17 15:39:41 +08:00
if(globalState.healthcheckLock) return;
2024-05-06 14:29:49 +08:00
for (final delay in appState.delayMap.entries) {
setDelay(
Delay(
name: delay.key,
value: 0,
),
);
}
clashCore.healthcheck();
}
2024-04-30 23:38:49 +08:00
setDelay(Delay delay) {
appState.setDelay(delay);
}
updateDelayMap() async {
appState.delayMap = await clashCore.getDelayMap();
}
2024-04-30 23:38:49 +08:00
toPage(int index, {bool hasAnimate = false}) {
final nextLabel = globalState.currentNavigationItems[index].label;
appState.currentLabel = nextLabel;
if ((config.isAnimateToPage || hasAnimate)) {
globalState.pageController?.animateToPage(
index,
duration: kTabScrollDuration,
curve: Curves.easeOut,
);
} else {
globalState.pageController?.jumpToPage(index);
}
}
updatePackages() async {
await globalState.updatePackages(appState);
}
toProfiles() {
final index = globalState.currentNavigationItems.indexWhere(
2024-05-06 10:32:39 +08:00
(element) => element.label == "profiles",
2024-04-30 23:38:49 +08:00
);
if (index != -1) {
toPage(index);
}
}
initLink() {
linkManager.initAppLinksListen(
2024-05-06 10:32:39 +08:00
(url) {
2024-04-30 23:38:49 +08:00
globalState.showMessage(
title: "${appLocalizations.add}${appLocalizations.profile}",
message: TextSpan(
children: [
TextSpan(text: appLocalizations.doYouWantToPass),
TextSpan(
text: " $url ",
style: TextStyle(
2024-05-06 10:32:39 +08:00
color: Theme.of(context).colorScheme.primary,
2024-04-30 23:38:49 +08:00
decoration: TextDecoration.underline,
2024-05-06 10:32:39 +08:00
decorationColor: Theme.of(context).colorScheme.primary,
2024-04-30 23:38:49 +08:00
),
),
TextSpan(
text:
2024-05-06 10:32:39 +08:00
"${appLocalizations.create}${appLocalizations.profile}"),
2024-04-30 23:38:49 +08:00
],
),
onTab: () {
addProfileFormURL(url);
},
);
},
);
}
addProfileFormURL(String url) async {
globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
toProfiles();
final commonScaffoldState = globalState.homeScaffoldKey.currentState;
if (commonScaffoldState?.mounted != true) return;
commonScaffoldState?.loadingRun(
() async {
await Future.delayed(const Duration(milliseconds: 300));
final profile = Profile(
url: url,
);
final res = await profile.update();
if (res.type == ResultType.success) {
addProfile(profile);
} else {
debugPrint(res.message);
globalState.showMessage(
title: "${appLocalizations.add}${appLocalizations.profile}",
message: TextSpan(text: res.message!),
);
}
},
);
}
2024-04-30 23:38:49 +08:00
addProfileFormFile() async {
final result = await picker.pickerConfigFile();
2024-04-30 23:38:49 +08:00
if (result.type == ResultType.error) return;
if (!context.mounted) return;
globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
toProfiles();
final commonScaffoldState = globalState.homeScaffoldKey.currentState;
if (commonScaffoldState?.mounted != true) return;
commonScaffoldState?.loadingRun(
2024-05-06 10:32:39 +08:00
() async {
2024-04-30 23:38:49 +08:00
await Future.delayed(const Duration(milliseconds: 300));
final bytes = result.data?.bytes;
if (bytes == null) {
return;
}
final profile = Profile(label: result.data?.name);
final sRes = await profile.saveFile(bytes);
if (sRes.type == ResultType.error) {
debugPrint(sRes.message);
globalState.showMessage(
title: "${appLocalizations.add}${appLocalizations.profile}",
message: TextSpan(text: sRes.message!),
);
return;
}
addProfile(profile);
},
);
}
addProfileFormQrCode() async {
final result = await picker.pickerConfigQRCode();
if (result.type == ResultType.error) {
if(result.message != null){
globalState.showMessage(
title: appLocalizations.tip,
message: TextSpan(
text: result.message,
),
);
}
return;
}
addProfileFormURL(result.data!);
}
clearShowProxyDelay() {
final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
if (showProxyDelay != null) {
appState.setDelay(
Delay(name: showProxyDelay, value: null),
);
}
}
2024-05-17 15:39:41 +08:00
testShowProxyDelay() {
final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
if (showProxyDelay != null) {
globalState.updateCurrentDelay(showProxyDelay);
}
}
2024-04-30 23:38:49 +08:00
}