Files
MWClash/lib/state.dart

345 lines
9.0 KiB
Dart
Raw Normal View History

2024-04-30 23:38:49 +08:00
import 'dart:async';
import 'dart:io';
import 'package:animations/animations.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/plugins/service.dart';
import 'package:fl_clash/plugins/vpn.dart';
2024-04-30 23:38:49 +08:00
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
2024-04-30 23:38:49 +08:00
import 'common/common.dart';
import 'controller.dart';
2024-04-30 23:38:49 +08:00
import 'models/models.dart';
class GlobalState {
Timer? timer;
Timer? groupsUpdateTimer;
2024-07-13 16:36:08 +08:00
var isVpnService = false;
late PackageInfo packageInfo;
Function? updateCurrentDelayDebounce;
2024-04-30 23:38:49 +08:00
PageController? pageController;
late Measure measure;
DateTime? startTime;
final safeMessageOffsetNotifier = ValueNotifier(Offset.zero);
2024-04-30 23:38:49 +08:00
final navigatorKey = GlobalKey<NavigatorState>();
late AppController appController;
2024-04-30 23:38:49 +08:00
GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
List<Function> updateFunctionLists = [];
bool lastTunEnable = false;
int? lastProfileModified;
2024-04-30 23:38:49 +08:00
bool get isStart => startTime != null && startTime!.isBeforeNow;
2024-04-30 23:38:49 +08:00
startListenUpdate() {
if (timer != null && timer!.isActive == true) return;
timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
for (final function in updateFunctionLists) {
function();
}
});
}
stopListenUpdate() {
if (timer == null || timer?.isActive == false) return;
timer?.cancel();
}
Future<void> initCore({
required AppState appState,
required ClashConfig clashConfig,
required Config config,
}) async {
await globalState.init(
appState: appState,
config: config,
clashConfig: clashConfig,
);
await applyProfile(
appState: appState,
config: config,
clashConfig: clashConfig,
);
}
Future<void> updateClashConfig({
required AppState appState,
2024-04-30 23:38:49 +08:00
required ClashConfig clashConfig,
required Config config,
bool isPatch = true,
}) async {
await config.currentProfile?.checkAndUpdate();
final useClashConfig = clashConfig.copyWith();
if (clashConfig.tun.enable != lastTunEnable &&
lastTunEnable == false &&
!Platform.isAndroid) {
final code = await system.authorizeCore();
switch (code) {
case AuthorizeCode.none:
break;
case AuthorizeCode.success:
lastTunEnable = useClashConfig.tun.enable;
await restartCore(
appState: appState,
clashConfig: clashConfig,
config: config,
);
return;
case AuthorizeCode.error:
useClashConfig.tun = useClashConfig.tun.copyWith(
enable: false,
);
}
}
if (config.appSetting.openLogs) {
clashCore.startLog();
} else {
clashCore.stopLog();
}
final res = await clashCore.updateConfig(
UpdateConfigParams(
profileId: config.currentProfileId ?? "",
config: useClashConfig,
params: ConfigExtendedParams(
isPatch: isPatch,
isCompatible: true,
selectedMap: config.currentSelectedMap,
overrideDns: config.overrideDns,
testUrl: config.appSetting.testUrl,
),
),
);
if (res.isNotEmpty) throw res;
lastTunEnable = useClashConfig.tun.enable;
lastProfileModified = await config.getCurrentProfile()?.profileLastModified;
2024-04-30 23:38:49 +08:00
}
handleStart() async {
await clashCore.startListener();
if (globalState.isVpnService) {
await vpn?.startVpn();
startListenUpdate();
return;
}
startTime ??= DateTime.now();
await service?.init();
2024-04-30 23:38:49 +08:00
startListenUpdate();
}
restartCore({
required AppState appState,
required ClashConfig clashConfig,
required Config config,
bool isPatch = true,
}) async {
await clashService?.startCore();
await initCore(
appState: appState,
clashConfig: clashConfig,
config: config,
);
if (isStart) {
await handleStart();
}
}
updateStartTime() {
startTime = clashLib?.getRunTime();
}
Future handleStop() async {
startTime = null;
await clashCore.stopListener();
clashLib?.stopTun();
await service?.destroy();
2024-04-30 23:38:49 +08:00
stopListenUpdate();
}
Future applyProfile({
2024-05-04 16:39:21 +08:00
required AppState appState,
required Config config,
required ClashConfig clashConfig,
}) async {
clashCore.requestGc();
await updateClashConfig(
appState: appState,
2024-05-04 16:39:21 +08:00
clashConfig: clashConfig,
config: config,
isPatch: false,
);
await updateGroups(appState);
await updateProviders(appState);
}
updateProviders(AppState appState) async {
appState.providers = await clashCore.getExternalProviders();
2024-05-04 16:39:21 +08:00
}
2024-04-30 23:38:49 +08:00
init({
required AppState appState,
required Config config,
required ClashConfig clashConfig,
}) async {
appState.isInit = await clashCore.isInit;
2024-04-30 23:38:49 +08:00
if (!appState.isInit) {
appState.isInit = await clashCore.init(
2024-04-30 23:38:49 +08:00
config: config,
clashConfig: clashConfig,
);
clashLib?.setState(
CoreState(
enable: config.vpnProps.enable,
accessControl: config.isAccessControl ? config.accessControl : null,
ipv6: config.vpnProps.ipv6,
allowBypass: config.vpnProps.allowBypass,
systemProxy: config.vpnProps.systemProxy,
onlyProxy: config.appSetting.onlyProxy,
bypassDomain: config.networkProps.bypassDomain,
routeAddress: clashConfig.routeAddress,
currentProfileName:
config.currentProfile?.label ?? config.currentProfileId ?? "",
),
);
}
}
2024-05-03 14:31:10 +08:00
Future<void> updateGroups(AppState appState) async {
2024-05-04 16:39:21 +08:00
appState.groups = await clashCore.getProxiesGroups();
2024-04-30 23:38:49 +08:00
}
showMessage({
required String title,
required InlineSpan message,
Function()? onTab,
2024-05-31 09:59:18 +08:00
String? confirmText,
2024-04-30 23:38:49 +08:00
}) {
showCommonDialog(
child: Builder(
builder: (context) {
return AlertDialog(
title: Text(title),
2024-05-31 09:59:18 +08:00
content: Container(
2024-04-30 23:38:49 +08:00
width: 300,
constraints: const BoxConstraints(maxHeight: 200),
2024-05-31 09:59:18 +08:00
child: SingleChildScrollView(
child: SelectableText.rich(
TextSpan(
2024-05-31 09:59:18 +08:00
style: Theme.of(context).textTheme.labelLarge,
children: [message],
),
style: const TextStyle(
overflow: TextOverflow.visible,
),
),
2024-04-30 23:38:49 +08:00
),
),
actions: [
TextButton(
onPressed: onTab ??
() {
Navigator.of(context).pop();
},
2024-05-31 09:59:18 +08:00
child: Text(confirmText ?? appLocalizations.confirm),
2024-04-30 23:38:49 +08:00
)
],
);
},
),
);
}
changeProxy({
required Config config,
required String groupName,
required String proxyName,
}) async {
await clashCore.changeProxy(
ChangeProxyParams(
groupName: groupName,
proxyName: proxyName,
),
);
if (config.appSetting.closeConnections) {
clashCore.closeConnections();
}
}
Future<T?> showCommonDialog<T>({
2024-04-30 23:38:49 +08:00
required Widget child,
bool dismissible = true,
2024-04-30 23:38:49 +08:00
}) async {
return await showModal<T>(
context: navigatorKey.currentState!.context,
configuration: FadeScaleTransitionConfiguration(
2024-04-30 23:38:49 +08:00
barrierColor: Colors.black38,
barrierDismissible: dismissible,
2024-04-30 23:38:49 +08:00
),
builder: (_) => child,
filter: filter,
2024-04-30 23:38:49 +08:00
);
}
2024-05-31 09:59:18 +08:00
2024-04-30 23:38:49 +08:00
updateTraffic({
required Config config,
AppFlowingState? appFlowingState,
}) async {
final onlyProxy = config.appSetting.onlyProxy;
final traffic = await clashCore.getTraffic(onlyProxy);
2024-07-13 16:36:08 +08:00
if (Platform.isAndroid && isVpnService == true) {
vpn?.startForeground(
title: clashLib?.getCurrentProfileName() ?? "",
2024-04-30 23:38:49 +08:00
content: "$traffic",
);
2024-07-13 16:36:08 +08:00
} else {
if (appFlowingState != null) {
appFlowingState.addTraffic(traffic);
appFlowingState.totalTraffic =
await clashCore.getTotalTraffic(onlyProxy);
2024-07-13 16:36:08 +08:00
}
2024-04-30 23:38:49 +08:00
}
}
Future<T?> safeRun<T>(
FutureOr<T> Function() futureFunction, {
String? title,
2025-01-09 18:38:52 +08:00
bool silence = true,
}) async {
try {
final res = await futureFunction();
return res;
} catch (e) {
2025-01-09 18:38:52 +08:00
if (silence) {
showNotifier(e.toString());
} else {
showMessage(
title: title ?? appLocalizations.tip,
message: TextSpan(
text: e.toString(),
),
);
}
return null;
}
}
showNotifier(String text) {
navigatorKey.currentContext?.showNotifier(text);
}
openUrl(String url) {
showMessage(
message: TextSpan(text: url),
title: appLocalizations.externalLink,
confirmText: appLocalizations.go,
onTab: () {
launchUrl(Uri.parse(url));
},
);
}
2024-04-30 23:38:49 +08:00
}
final globalState = GlobalState();