Add android separates the core process
Support core status check and force restart Optimize proxies page and access page Update flutter and pub dependencies Update go version Optimize more details
This commit is contained in:
+219
-331
@@ -4,8 +4,8 @@ import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/archive.dart';
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
@@ -20,7 +20,6 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'common/common.dart';
|
||||
import 'models/models.dart';
|
||||
import 'views/profiles/override_profile.dart';
|
||||
|
||||
class AppController {
|
||||
int? lastProfileModified;
|
||||
@@ -42,8 +41,8 @@ class AppController {
|
||||
});
|
||||
}
|
||||
|
||||
void updateGroupsDebounce() {
|
||||
debouncer.call(FunctionTag.updateGroups, updateGroups);
|
||||
void updateGroupsDebounce([Duration? duration]) {
|
||||
debouncer.call(FunctionTag.updateGroups, updateGroups, duration: duration);
|
||||
}
|
||||
|
||||
void addCheckIpNumDebounce() {
|
||||
@@ -52,9 +51,7 @@ class AppController {
|
||||
});
|
||||
}
|
||||
|
||||
void applyProfileDebounce({
|
||||
bool silence = false,
|
||||
}) {
|
||||
void applyProfileDebounce({bool silence = false}) {
|
||||
debouncer.call(FunctionTag.applyProfile, (silence) {
|
||||
applyProfile(silence: silence);
|
||||
}, args: [silence]);
|
||||
@@ -65,33 +62,32 @@ class AppController {
|
||||
}
|
||||
|
||||
void changeProxyDebounce(String groupName, String proxyName) {
|
||||
debouncer.call(FunctionTag.changeProxy,
|
||||
(String groupName, String proxyName) async {
|
||||
await changeProxy(
|
||||
groupName: groupName,
|
||||
proxyName: proxyName,
|
||||
);
|
||||
debouncer.call(FunctionTag.changeProxy, (
|
||||
String groupName,
|
||||
String proxyName,
|
||||
) async {
|
||||
await changeProxy(groupName: groupName, proxyName: proxyName);
|
||||
await updateGroups();
|
||||
}, args: [groupName, proxyName]);
|
||||
}
|
||||
|
||||
Future<void> restartCore() async {
|
||||
commonPrint.log('restart core');
|
||||
await clashService?.reStart();
|
||||
globalState.isUserDisconnected = true;
|
||||
await coreController.shutdown();
|
||||
await _connectCore();
|
||||
await _initCore();
|
||||
if (_ref.read(runTimeProvider.notifier).isStart) {
|
||||
_ref.read(initProvider.notifier).value = true;
|
||||
if (_ref.read(isStartProvider)) {
|
||||
await globalState.handleStart();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateStatus(bool isStart) async {
|
||||
if (isStart) {
|
||||
await globalState.handleStart([
|
||||
updateRunTime,
|
||||
updateTraffic,
|
||||
]);
|
||||
final currentLastModified =
|
||||
await _ref.read(currentProfileProvider)?.profileLastModified;
|
||||
await globalState.handleStart([updateRunTime, updateTraffic]);
|
||||
final currentLastModified = await _ref
|
||||
.read(currentProfileProvider)
|
||||
?.profileLastModified;
|
||||
if (currentLastModified == null || lastProfileModified == null) {
|
||||
addCheckIpNumDebounce();
|
||||
return;
|
||||
@@ -103,7 +99,7 @@ class AppController {
|
||||
applyProfileDebounce();
|
||||
} else {
|
||||
await globalState.handleStop();
|
||||
clashCore.resetTraffic();
|
||||
coreController.resetTraffic();
|
||||
_ref.read(trafficsProvider.notifier).clear();
|
||||
_ref.read(totalTrafficProvider.notifier).value = Traffic();
|
||||
_ref.read(runTimeProvider.notifier).value = null;
|
||||
@@ -123,10 +119,13 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> updateTraffic() async {
|
||||
final traffic = await clashCore.getTraffic();
|
||||
final onlyStatisticsProxy = _ref.read(
|
||||
appSettingProvider.select((state) => state.onlyStatisticsProxy),
|
||||
);
|
||||
final traffic = await coreController.getTraffic(onlyStatisticsProxy);
|
||||
_ref.read(trafficsProvider.notifier).addTraffic(traffic);
|
||||
_ref.read(totalTrafficProvider.notifier).value =
|
||||
await clashCore.getTotalTraffic();
|
||||
_ref.read(totalTrafficProvider.notifier).value = await coreController
|
||||
.getTotalTraffic(onlyStatisticsProxy);
|
||||
}
|
||||
|
||||
Future<void> addProfile(Profile profile) async {
|
||||
@@ -152,8 +151,8 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> updateProviders() async {
|
||||
_ref.read(providersProvider.notifier).value =
|
||||
await clashCore.getExternalProviders();
|
||||
_ref.read(providersProvider.notifier).value = await coreController
|
||||
.getExternalProviders();
|
||||
}
|
||||
|
||||
Future<void> updateLocalIp() async {
|
||||
@@ -193,8 +192,9 @@ class AppController {
|
||||
|
||||
void updateOrAddHotKeyAction(HotKeyAction hotKeyAction) {
|
||||
final hotKeyActions = _ref.read(hotKeyActionsProvider);
|
||||
final index =
|
||||
hotKeyActions.indexWhere((item) => item.action == hotKeyAction.action);
|
||||
final index = hotKeyActions.indexWhere(
|
||||
(item) => item.action == hotKeyAction.action,
|
||||
);
|
||||
if (index == -1) {
|
||||
_ref.read(hotKeyActionsProvider.notifier).value = List.from(hotKeyActions)
|
||||
..add(hotKeyAction);
|
||||
@@ -225,16 +225,12 @@ class AppController {
|
||||
}
|
||||
|
||||
String? getCurrentGroupName() {
|
||||
final currentGroupName = _ref.read(currentProfileProvider.select(
|
||||
(state) => state?.currentGroupName,
|
||||
));
|
||||
final currentGroupName = _ref.read(
|
||||
currentProfileProvider.select((state) => state?.currentGroupName),
|
||||
);
|
||||
return currentGroupName;
|
||||
}
|
||||
|
||||
ProxyCardState getProxyCardState(String proxyName) {
|
||||
return _ref.read(getProxyCardStateProvider(proxyName));
|
||||
}
|
||||
|
||||
String? getSelectedProxyName(String groupName) {
|
||||
return _ref.read(getSelectedProxyNameProvider(groupName));
|
||||
}
|
||||
@@ -244,18 +240,13 @@ class AppController {
|
||||
if (profile == null || profile.currentGroupName == groupName) {
|
||||
return;
|
||||
}
|
||||
setProfile(
|
||||
profile.copyWith(currentGroupName: groupName),
|
||||
);
|
||||
setProfile(profile.copyWith(currentGroupName: groupName));
|
||||
}
|
||||
|
||||
Future<void> updateClashConfig() async {
|
||||
await safeRun(
|
||||
() async {
|
||||
await _updateClashConfig();
|
||||
},
|
||||
needLoading: true,
|
||||
);
|
||||
await safeRun(() async {
|
||||
await _updateClashConfig();
|
||||
}, needLoading: true);
|
||||
}
|
||||
|
||||
Future<void> _updateClashConfig() async {
|
||||
@@ -265,16 +256,14 @@ class AppController {
|
||||
return;
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
final message = await clashCore.updateConfig(
|
||||
updateParams.copyWith.tun(
|
||||
enable: realTunEnable,
|
||||
),
|
||||
final message = await coreController.updateConfig(
|
||||
updateParams.copyWith.tun(enable: realTunEnable),
|
||||
);
|
||||
if (message.isNotEmpty) throw message;
|
||||
}
|
||||
|
||||
Future<Result<bool>> _requestAdmin(bool enableTun) async {
|
||||
if(system.isWindows && kDebugMode){
|
||||
if (system.isWindows && kDebugMode) {
|
||||
return Result.success(false);
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
@@ -296,12 +285,9 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> setupClashConfig() async {
|
||||
await safeRun(
|
||||
() async {
|
||||
await _setupClashConfig();
|
||||
},
|
||||
needLoading: true,
|
||||
);
|
||||
await safeRun(() async {
|
||||
await _setupClashConfig();
|
||||
}, needLoading: true);
|
||||
}
|
||||
|
||||
Future<void> _setupClashConfig() async {
|
||||
@@ -313,14 +299,9 @@ class AppController {
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable);
|
||||
final params = await globalState.getSetupParams(
|
||||
pathConfig: realPatchConfig,
|
||||
);
|
||||
final message = await clashCore.setupConfig(params);
|
||||
final message = await coreController.setupConfig(realPatchConfig);
|
||||
lastProfileModified = await _ref.read(
|
||||
currentProfileProvider.select(
|
||||
(state) => state?.profileLastModified,
|
||||
),
|
||||
currentProfileProvider.select((state) => state?.profileLastModified),
|
||||
);
|
||||
if (message.isNotEmpty) {
|
||||
throw message;
|
||||
@@ -328,7 +309,7 @@ class AppController {
|
||||
}
|
||||
|
||||
Future _applyProfile() async {
|
||||
await clashCore.requestGc();
|
||||
await coreController.requestGc();
|
||||
await setupClashConfig();
|
||||
await updateGroups();
|
||||
await updateProviders();
|
||||
@@ -338,12 +319,9 @@ class AppController {
|
||||
if (silence) {
|
||||
await _applyProfile();
|
||||
} else {
|
||||
await safeRun(
|
||||
() async {
|
||||
await _applyProfile();
|
||||
},
|
||||
needLoading: true,
|
||||
);
|
||||
await safeRun(() async {
|
||||
await _applyProfile();
|
||||
}, needLoading: true);
|
||||
}
|
||||
addCheckIpNumDebounce();
|
||||
}
|
||||
@@ -367,9 +345,7 @@ class AppController {
|
||||
for (final profile in _ref.read(profilesProvider)) {
|
||||
if (!profile.autoUpdate) continue;
|
||||
final isNotNeedUpdate = profile.lastUpdateDate
|
||||
?.add(
|
||||
profile.autoUpdateDuration,
|
||||
)
|
||||
?.add(profile.autoUpdateDuration)
|
||||
.isBeforeNow;
|
||||
if (isNotNeedUpdate == false || profile.type == ProfileType.file) {
|
||||
continue;
|
||||
@@ -377,7 +353,7 @@ class AppController {
|
||||
try {
|
||||
await updateProfile(profile);
|
||||
} catch (e) {
|
||||
commonPrint.log(e.toString());
|
||||
commonPrint.log(e.toString(), logLevel: LogLevel.warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,7 +362,22 @@ class AppController {
|
||||
try {
|
||||
_ref.read(groupsProvider.notifier).value = await retry(
|
||||
task: () async {
|
||||
return await clashCore.getProxiesGroups();
|
||||
final sortType = _ref.read(
|
||||
proxiesStyleSettingProvider.select((state) => state.sortType),
|
||||
);
|
||||
final delayMap = _ref.read(delayDataSourceProvider);
|
||||
final testUrl = _ref.read(
|
||||
appSettingProvider.select((state) => state.testUrl),
|
||||
);
|
||||
final selectedMap = _ref.read(
|
||||
currentProfileProvider.select((state) => state?.selectedMap ?? {}),
|
||||
);
|
||||
return await coreController.getProxiesGroups(
|
||||
selectedMap: selectedMap,
|
||||
sortType: sortType,
|
||||
delayMap: delayMap,
|
||||
defaultTestUrl: testUrl,
|
||||
);
|
||||
},
|
||||
retryIf: (res) => res.isEmpty,
|
||||
);
|
||||
@@ -413,14 +404,11 @@ class AppController {
|
||||
required String groupName,
|
||||
required String proxyName,
|
||||
}) async {
|
||||
await clashCore.changeProxy(
|
||||
ChangeProxyParams(
|
||||
groupName: groupName,
|
||||
proxyName: proxyName,
|
||||
),
|
||||
await coreController.changeProxy(
|
||||
ChangeProxyParams(groupName: groupName, proxyName: proxyName),
|
||||
);
|
||||
if (_ref.read(appSettingProvider).closeConnections) {
|
||||
clashCore.closeConnections();
|
||||
coreController.closeConnections();
|
||||
}
|
||||
addCheckIpNumDebounce();
|
||||
}
|
||||
@@ -455,8 +443,8 @@ class AppController {
|
||||
await savePreferences();
|
||||
await macOS?.updateDns(true);
|
||||
await proxy?.stopProxy();
|
||||
await clashCore.shutdown();
|
||||
await clashService?.destroy();
|
||||
await coreController.shutdown();
|
||||
await coreController.destroy();
|
||||
} finally {
|
||||
system.exit();
|
||||
}
|
||||
@@ -465,9 +453,7 @@ class AppController {
|
||||
Future handleClear() async {
|
||||
await preferences.clearPreferences();
|
||||
commonPrint.log('clear preferences');
|
||||
globalState.config = Config(
|
||||
themeProps: defaultThemeProps,
|
||||
);
|
||||
globalState.config = Config(themeProps: defaultThemeProps);
|
||||
}
|
||||
|
||||
Future<void> autoCheckUpdate() async {
|
||||
@@ -494,15 +480,9 @@ class AppController {
|
||||
text: '$tagName \n',
|
||||
style: textTheme.headlineSmall,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '\n',
|
||||
style: textTheme.bodyMedium,
|
||||
),
|
||||
TextSpan(text: '\n', style: textTheme.bodyMedium),
|
||||
for (final submit in submits)
|
||||
TextSpan(
|
||||
text: '- $submit \n',
|
||||
style: textTheme.bodyMedium,
|
||||
),
|
||||
TextSpan(text: '- $submit \n', style: textTheme.bodyMedium),
|
||||
],
|
||||
),
|
||||
confirmText: appLocalizations.goDownload,
|
||||
@@ -510,15 +490,11 @@ class AppController {
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
launchUrl(
|
||||
Uri.parse('https://github.com/$repository/releases/latest'),
|
||||
);
|
||||
launchUrl(Uri.parse('https://github.com/$repository/releases/latest'));
|
||||
} else if (handleError) {
|
||||
globalState.showMessage(
|
||||
title: appLocalizations.checkUpdate,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.checkUpdateError,
|
||||
),
|
||||
message: TextSpan(text: appLocalizations.checkUpdateError),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -542,30 +518,24 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> _initCore() async {
|
||||
final isInit = await clashCore.isInit;
|
||||
final isInit = await coreController.isInit;
|
||||
if (!isInit) {
|
||||
await clashCore.init();
|
||||
await clashCore.setState(
|
||||
globalState.getCoreState(),
|
||||
);
|
||||
await coreController.init(globalState.appState.version);
|
||||
}
|
||||
await applyProfile();
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
FlutterError.onError = (details) {
|
||||
if (kDebugMode) {
|
||||
commonPrint.log(details.stack.toString());
|
||||
}
|
||||
commonPrint.log(
|
||||
'exception: ${details.exception} stack: ${details.stack}',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
};
|
||||
updateTray(true);
|
||||
await _initCore();
|
||||
await _initStatus();
|
||||
autoLaunch?.updateStatus(
|
||||
_ref.read(appSettingProvider).autoLaunch,
|
||||
);
|
||||
autoUpdateProfiles();
|
||||
autoCheckUpdate();
|
||||
autoLaunch?.updateStatus(_ref.read(appSettingProvider).autoLaunch);
|
||||
if (!_ref.read(appSettingProvider).silentLaunch) {
|
||||
window?.show();
|
||||
} else {
|
||||
@@ -573,9 +543,31 @@ class AppController {
|
||||
}
|
||||
await _handlePreference();
|
||||
await _handlerDisclaimer();
|
||||
await _showCrashlyticsTip();
|
||||
await _connectCore();
|
||||
await _initCore();
|
||||
await _initStatus();
|
||||
_ref.read(initProvider.notifier).value = true;
|
||||
}
|
||||
|
||||
Future<void> _connectCore() async {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connecting;
|
||||
final result = await Future.wait([
|
||||
coreController.preload(),
|
||||
if (!globalState.isService) Future.delayed(Duration(milliseconds: 300)),
|
||||
]);
|
||||
final String message = result[0];
|
||||
await Future.delayed(commonDuration);
|
||||
if (message.isNotEmpty) {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
if (context.mounted) {
|
||||
context.showNotifier(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connected;
|
||||
}
|
||||
|
||||
Future<void> _initStatus() async {
|
||||
if (system.isAndroid) {
|
||||
await globalState.updateStartTime();
|
||||
@@ -603,34 +595,32 @@ class AppController {
|
||||
}
|
||||
|
||||
void initLink() {
|
||||
linkManager.initAppLinksListen(
|
||||
(url) async {
|
||||
final res = await 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,
|
||||
),
|
||||
linkManager.initAppLinksListen((url) async {
|
||||
final res = await 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}'),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
TextSpan(
|
||||
text: '${appLocalizations.create}${appLocalizations.profile}',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
addProfileFormURL(url);
|
||||
},
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
addProfileFormURL(url);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> showDisclaimer() async {
|
||||
@@ -647,30 +637,47 @@ class AppController {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(disclaimerAccepted: true),
|
||||
);
|
||||
Navigator.of(context).pop<bool>(true);
|
||||
},
|
||||
child: Text(appLocalizations.agree),
|
||||
)
|
||||
),
|
||||
],
|
||||
child: SelectableText(
|
||||
appLocalizations.disclaimerDesc,
|
||||
),
|
||||
child: Text(appLocalizations.disclaimerDesc),
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
Future<void> _showCrashlyticsTip() async {
|
||||
if (!system.isAndroid) {
|
||||
return;
|
||||
}
|
||||
if (_ref.read(appSettingProvider.select((state) => state.crashlyticsTip))) {
|
||||
return;
|
||||
}
|
||||
await globalState.showMessage(
|
||||
title: appLocalizations.dataCollectionTip,
|
||||
cancelable: false,
|
||||
message: TextSpan(text: appLocalizations.dataCollectionContent),
|
||||
);
|
||||
_ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(crashlyticsTip: true));
|
||||
}
|
||||
|
||||
Future<void> _handlerDisclaimer() async {
|
||||
if (_ref.read(appSettingProvider).disclaimerAccepted) {
|
||||
if (_ref.read(
|
||||
appSettingProvider.select((state) => state.disclaimerAccepted),
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
final isDisclaimerAccepted = await showDisclaimer();
|
||||
if (!isDisclaimerAccepted) {
|
||||
await handleExit();
|
||||
}
|
||||
_ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(disclaimerAccepted: true));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -682,9 +689,7 @@ class AppController {
|
||||
|
||||
final profile = await safeRun(
|
||||
() async {
|
||||
return await Profile.normal(
|
||||
url: url,
|
||||
).update();
|
||||
return await Profile.normal(url: url).update();
|
||||
},
|
||||
needLoading: true,
|
||||
title: '${appLocalizations.add}${appLocalizations.profile}',
|
||||
@@ -718,9 +723,7 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> addProfileFormQrCode() async {
|
||||
final url = await safeRun(
|
||||
picker.pickerConfigQRCode,
|
||||
);
|
||||
final url = await safeRun(picker.pickerConfigQRCode);
|
||||
if (url == null) return;
|
||||
addProfileFormURL(url);
|
||||
}
|
||||
@@ -735,63 +738,7 @@ class AppController {
|
||||
_ref.read(providersProvider.notifier).setProvider(provider);
|
||||
}
|
||||
|
||||
List<Proxy> _sortOfName(List<Proxy> proxies) {
|
||||
return List.of(proxies)
|
||||
..sort(
|
||||
(a, b) => utils.sortByChar(
|
||||
utils.getPinyin(a.name),
|
||||
utils.getPinyin(b.name),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Proxy> _sortOfDelay({
|
||||
required List<Proxy> proxies,
|
||||
String? testUrl,
|
||||
}) {
|
||||
return List.of(proxies)
|
||||
..sort(
|
||||
(a, b) {
|
||||
final aDelay = _ref.read(getDelayProvider(
|
||||
proxyName: a.name,
|
||||
testUrl: testUrl,
|
||||
));
|
||||
final bDelay = _ref.read(
|
||||
getDelayProvider(
|
||||
proxyName: b.name,
|
||||
testUrl: testUrl,
|
||||
),
|
||||
);
|
||||
if (aDelay == null && bDelay == null) {
|
||||
return 0;
|
||||
}
|
||||
if (aDelay == null || aDelay == -1) {
|
||||
return 1;
|
||||
}
|
||||
if (bDelay == null || bDelay == -1) {
|
||||
return -1;
|
||||
}
|
||||
return aDelay.compareTo(bDelay);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<Proxy> getSortProxies({
|
||||
required List<Proxy> proxies,
|
||||
required ProxiesSortType sortType,
|
||||
String? testUrl,
|
||||
}) {
|
||||
return switch (sortType) {
|
||||
ProxiesSortType.none => proxies,
|
||||
ProxiesSortType.delay => _sortOfDelay(
|
||||
proxies: proxies,
|
||||
testUrl: testUrl,
|
||||
),
|
||||
ProxiesSortType.name => _sortOfName(proxies),
|
||||
};
|
||||
}
|
||||
|
||||
Future<Null> clearEffect(String profileId) async {
|
||||
Future<void> clearEffect(String profileId) async {
|
||||
final profilePath = await appPath.getProfilePath(profileId);
|
||||
final providersDirPath = await appPath.getProvidersDirPath(profileId);
|
||||
return await Isolate.run(() async {
|
||||
@@ -800,28 +747,28 @@ class AppController {
|
||||
if (isExists) {
|
||||
profileFile.delete(recursive: true);
|
||||
}
|
||||
final providersFileDir = File(providersDirPath);
|
||||
final providersFileIsExists = await providersFileDir.exists();
|
||||
if (providersFileIsExists) {
|
||||
providersFileDir.delete(recursive: true);
|
||||
}
|
||||
await coreController.deleteFile(providersDirPath);
|
||||
});
|
||||
}
|
||||
|
||||
void updateTun() {
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.tun(enable: !state.tun.enable),
|
||||
);
|
||||
_ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith.tun(enable: !state.tun.enable));
|
||||
}
|
||||
|
||||
void updateSystemProxy() {
|
||||
_ref.read(networkSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
systemProxy: !state.systemProxy,
|
||||
),
|
||||
_ref
|
||||
.read(networkSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(systemProxy: !state.systemProxy),
|
||||
);
|
||||
}
|
||||
|
||||
void handleCoreDisconnected() {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
}
|
||||
|
||||
Future<List<Package>> getPackages() async {
|
||||
if (_ref.read(isMobileViewProvider)) {
|
||||
await Future.delayed(commonDuration);
|
||||
@@ -834,21 +781,18 @@ class AppController {
|
||||
}
|
||||
|
||||
void updateStart() {
|
||||
updateStatus(!_ref.read(runTimeProvider.notifier).isStart);
|
||||
updateStatus(!_ref.read(isStartProvider));
|
||||
}
|
||||
|
||||
void updateCurrentSelectedMap(String groupName, String proxyName) {
|
||||
final currentProfile = _ref.read(currentProfileProvider);
|
||||
if (currentProfile != null &&
|
||||
currentProfile.selectedMap[groupName] != proxyName) {
|
||||
final SelectedMap selectedMap = Map.from(
|
||||
currentProfile.selectedMap,
|
||||
)..[groupName] = proxyName;
|
||||
_ref.read(profilesProvider.notifier).setProfile(
|
||||
currentProfile.copyWith(
|
||||
selectedMap: selectedMap,
|
||||
),
|
||||
);
|
||||
final SelectedMap selectedMap = Map.from(currentProfile.selectedMap)
|
||||
..[groupName] = proxyName;
|
||||
_ref
|
||||
.read(profilesProvider.notifier)
|
||||
.setProfile(currentProfile.copyWith(selectedMap: selectedMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,17 +801,15 @@ class AppController {
|
||||
if (currentProfile == null) {
|
||||
return;
|
||||
}
|
||||
_ref.read(profilesProvider.notifier).setProfile(
|
||||
currentProfile.copyWith(
|
||||
unfoldSet: value,
|
||||
),
|
||||
);
|
||||
_ref
|
||||
.read(profilesProvider.notifier)
|
||||
.setProfile(currentProfile.copyWith(unfoldSet: value));
|
||||
}
|
||||
|
||||
void changeMode(Mode mode) {
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(mode: mode),
|
||||
);
|
||||
_ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(mode: mode));
|
||||
if (mode == Mode.global) {
|
||||
updateCurrentGroupName(GroupName.GLOBAL.name);
|
||||
}
|
||||
@@ -875,11 +817,9 @@ class AppController {
|
||||
}
|
||||
|
||||
void updateAutoLaunch() {
|
||||
_ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
autoLaunch: !state.autoLaunch,
|
||||
),
|
||||
);
|
||||
_ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(autoLaunch: !state.autoLaunch));
|
||||
}
|
||||
|
||||
Future<void> updateVisible() async {
|
||||
@@ -892,64 +832,23 @@ class AppController {
|
||||
}
|
||||
|
||||
void updateMode() {
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) {
|
||||
final index = Mode.values.indexWhere((item) => item == state.mode);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1;
|
||||
return state.copyWith(
|
||||
mode: Mode.values[nextIndex],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleAddOrUpdate(WidgetRef ref, [Rule? rule]) async {
|
||||
final res = await globalState.showCommonDialog<Rule>(
|
||||
child: AddRuleDialog(
|
||||
rule: rule,
|
||||
snippet: ref.read(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) => state.snippet!,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (res == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) {
|
||||
final model = state.copyWith.overrideData!(
|
||||
rule: state.overrideData!.rule.updateRules(
|
||||
(rules) {
|
||||
final index = rules.indexWhere((item) => item.id == res.id);
|
||||
if (index == -1) {
|
||||
return List.from([res, ...rules]);
|
||||
}
|
||||
return List.from(rules)..[index] = res;
|
||||
},
|
||||
),
|
||||
);
|
||||
return model;
|
||||
},
|
||||
);
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState((state) {
|
||||
final index = Mode.values.indexWhere((item) => item == state.mode);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1;
|
||||
return state.copyWith(mode: Mode.values[nextIndex]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> exportLogs() async {
|
||||
final logsRaw = _ref.read(logsProvider).list.map(
|
||||
(item) => item.toString(),
|
||||
);
|
||||
final logsRaw = _ref.read(logsProvider).list.map((item) => item.toString());
|
||||
final data = await Isolate.run<List<int>>(() async {
|
||||
final logsRawString = logsRaw.join('\n');
|
||||
return utf8.encode(logsRawString);
|
||||
});
|
||||
return await picker.saveFile(
|
||||
utils.logFile,
|
||||
Uint8List.fromList(data),
|
||||
) !=
|
||||
return await picker.saveFile(utils.logFile, Uint8List.fromList(data)) !=
|
||||
null;
|
||||
}
|
||||
|
||||
@@ -959,17 +858,15 @@ class AppController {
|
||||
final configJson = globalState.config.toJson();
|
||||
return Isolate.run<List<int>>(() async {
|
||||
final archive = Archive();
|
||||
archive.add('config.json', configJson);
|
||||
archive.addTextFile('config.json', configJson);
|
||||
archive.addDirectoryToArchive(profilesPath, homeDirPath);
|
||||
final zipEncoder = ZipEncoder();
|
||||
return zipEncoder.encode(archive) ?? [];
|
||||
return zipEncoder.encode(archive);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateTray([bool focus = false]) async {
|
||||
tray.update(
|
||||
trayState: _ref.read(trayStateProvider),
|
||||
);
|
||||
tray?.update(trayState: _ref.read(trayStateProvider));
|
||||
}
|
||||
|
||||
Future<void> recoveryData(
|
||||
@@ -981,18 +878,19 @@ class AppController {
|
||||
return zipDecoder.decodeBytes(data);
|
||||
});
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
final configs =
|
||||
archive.files.where((item) => item.name.endsWith('.json')).toList();
|
||||
final profiles =
|
||||
archive.files.where((item) => !item.name.endsWith('.json'));
|
||||
final configIndex =
|
||||
configs.indexWhere((config) => config.name == 'config.json');
|
||||
final configs = archive.files
|
||||
.where((item) => item.name.endsWith('.json'))
|
||||
.toList();
|
||||
final profiles = archive.files.where(
|
||||
(item) => !item.name.endsWith('.json'),
|
||||
);
|
||||
final configIndex = configs.indexWhere(
|
||||
(config) => config.name == 'config.json',
|
||||
);
|
||||
if (configIndex == -1) throw 'invalid backup file';
|
||||
final configFile = configs[configIndex];
|
||||
var tempConfig = Config.compatibleFromJson(
|
||||
json.decode(
|
||||
utf8.decode(configFile.content),
|
||||
),
|
||||
json.decode(utf8.decode(configFile.content)),
|
||||
);
|
||||
for (final profile in profiles) {
|
||||
final filePath = join(homeDirPath, profile.name);
|
||||
@@ -1000,38 +898,30 @@ class AppController {
|
||||
await file.create(recursive: true);
|
||||
await file.writeAsBytes(profile.content);
|
||||
}
|
||||
final clashConfigIndex =
|
||||
configs.indexWhere((config) => config.name == 'clashConfig.json');
|
||||
final clashConfigIndex = configs.indexWhere(
|
||||
(config) => config.name == 'clashConfig.json',
|
||||
);
|
||||
if (clashConfigIndex != -1) {
|
||||
final clashConfigFile = configs[clashConfigIndex];
|
||||
tempConfig = tempConfig.copyWith(
|
||||
patchClashConfig: ClashConfig.fromJson(
|
||||
json.decode(
|
||||
utf8.decode(
|
||||
clashConfigFile.content,
|
||||
),
|
||||
),
|
||||
json.decode(utf8.decode(clashConfigFile.content)),
|
||||
),
|
||||
);
|
||||
}
|
||||
_recovery(
|
||||
tempConfig,
|
||||
recoveryOption,
|
||||
);
|
||||
_recovery(tempConfig, recoveryOption);
|
||||
}
|
||||
|
||||
void _recovery(Config config, RecoveryOption recoveryOption) {
|
||||
final recoveryStrategy = _ref.read(appSettingProvider.select(
|
||||
(state) => state.recoveryStrategy,
|
||||
));
|
||||
final recoveryStrategy = _ref.read(
|
||||
appSettingProvider.select((state) => state.recoveryStrategy),
|
||||
);
|
||||
final profiles = config.profiles;
|
||||
if (recoveryStrategy == RecoveryStrategy.override) {
|
||||
_ref.read(profilesProvider.notifier).value = profiles;
|
||||
} else {
|
||||
for (final profile in profiles) {
|
||||
_ref.read(profilesProvider.notifier).setProfile(
|
||||
profile,
|
||||
);
|
||||
_ref.read(profilesProvider.notifier).setProfile(profile);
|
||||
}
|
||||
}
|
||||
final onlyProfiles = recoveryOption == RecoveryOption.onlyProfiles;
|
||||
@@ -1072,15 +962,13 @@ class AppController {
|
||||
final res = await futureFunction();
|
||||
return res;
|
||||
} catch (e) {
|
||||
commonPrint.log('$e');
|
||||
commonPrint.log('$futureFunction ===> $e', logLevel: LogLevel.warning);
|
||||
if (realSilence) {
|
||||
globalState.showNotifier(e.toString());
|
||||
} else {
|
||||
globalState.showMessage(
|
||||
title: title ?? appLocalizations.tip,
|
||||
message: TextSpan(
|
||||
text: e.toString(),
|
||||
),
|
||||
message: TextSpan(text: e.toString()),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user