Files
MWClash/lib/controller.dart
2024-05-03 15:33:45 +08:00

340 lines
8.8 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'dart:isolate';
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,
];
} else {
await globalState.stopSystemProxy();
appState.traffics = [];
appState.runTime = null;
}
}
updateCoreVersionInfo() {
globalState.updateCoreVersionInfo(appState);
}
updateRunTime() {
if (proxyManager.startTime != null) {
final startTimeStamp = proxyManager.startTime!.millisecondsSinceEpoch;
final nowTimeStamp = DateTime.now().millisecondsSinceEpoch;
appState.runTime = nowTimeStamp - startTimeStamp;
} else {
appState.runTime = null;
}
}
updateTraffic() {
globalState.updateTraffic(
config: config,
appState: appState,
);
}
changeProxy() {
globalState.changeProxy(
appState: appState,
config: config,
clashConfig: clashConfig,
);
}
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;
final file = File(profilePath);
Isolate.run(() async {
final isExists = await file.exists();
if (isExists) {
file.delete();
}
});
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);
}
}
}
Future<bool> updateClashConfig({bool isPatch = true}) async {
return await globalState.updateClashConfig(
clashConfig: clashConfig,
config: config,
isPatch: isPatch,
);
}
changeProfile(String? value) async {
if (value == config.currentProfileId) return;
config.currentProfileId = value;
await updateClashConfig(isPatch: false);
await updateGroups();
changeProxy();
appState.delayMap = {};
saveConfigPreferences();
}
autoUpdateProfiles() async {
for (final profile in config.profiles) {
if (!profile.autoUpdate) return;
final isNotNeedUpdate = profile.lastUpdateDate
?.add(
profile.autoUpdateDuration,
)
.isBeforeNow();
if (isNotNeedUpdate == false) continue;
final result = await profile.update();
if (result.type == ResultType.error) continue;
await updateGroups();
changeProxy();
}
}
Future<void> updateGroups() async {
await globalState.updateGroups(appState);
}
updateSystemColorSchemes(SystemColorSchemes systemColorSchemes) {
appState.systemColorSchemes = systemColorSchemes;
}
clearCurrentDelay() {
final currentProxyName =
appState.getCurrentProxyName(config.currentProxyName, clashConfig.mode);
if (currentProxyName == null) return;
appState.setDelay(Delay(name: currentProxyName, value: null));
}
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 {
if (appState.isInit) {
if (config.autoRun) {
await updateSystemProxy(true);
} else {
await proxyManager.updateStartTime();
await updateSystemProxy(proxyManager.isStart);
}
autoUpdateProfiles();
updateLogStatus();
if (!config.silentLaunch) {
window?.show();
}
}
}
setDelay(Delay delay) {
appState.setDelay(delay);
}
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(
(element) => element.label == "profiles",
);
if (index != -1) {
toPage(index);
}
}
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!),
);
}
},
);
}
initLink() {
linkManager.initAppLinksListen(
(url) {
globalState.showMessage(
title: "${appLocalizations.add}${appLocalizations.profile}",
message: TextSpan(
children: [
TextSpan(text: appLocalizations.doYouWantToPass),
TextSpan(
text: " $url ",
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
decorationColor: Theme.of(context).colorScheme.primary,
),
),
TextSpan(
text:
"${appLocalizations.create}${appLocalizations.profile}"),
],
),
onTab: () {
addProfileFormURL(url);
},
);
},
);
}
addProfileFormFile() async {
final result = await FileUtil.pickerConfig();
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(
() async {
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);
},
);
}
}