Fix android vpn close issues
Add requests page Fix checkUpdate dark mode style error Fix quickStart error open app Add memory proxies tab index Support hidden group Optimize logs
This commit is contained in:
@@ -82,6 +82,10 @@ class ApplicationState extends State<Application> {
|
||||
super.initState();
|
||||
globalState.appController = AppController(context);
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final currentContext = globalState.navigatorKey.currentContext;
|
||||
if (currentContext != null) {
|
||||
globalState.appController = AppController(currentContext);
|
||||
}
|
||||
await globalState.appController.init();
|
||||
globalState.appController.initLink();
|
||||
_updateGroups();
|
||||
|
||||
@@ -100,7 +100,7 @@ class ClashCore {
|
||||
UsedProxy.GLOBAL.name,
|
||||
...(proxies[UsedProxy.GLOBAL.name]["all"] as List).where((e) {
|
||||
final proxy = proxies[e];
|
||||
return GroupTypeExtension.valueList.contains(proxy['type']);
|
||||
return GroupTypeExtension.valueList.contains(proxy['type']) && proxy['hidden'] != true;
|
||||
})
|
||||
];
|
||||
final groupsRaw = groupNames.map((groupName) {
|
||||
@@ -210,7 +210,7 @@ class ClashCore {
|
||||
clashFFI.startTUN(fd);
|
||||
}
|
||||
|
||||
requestGc(){
|
||||
requestGc() {
|
||||
clashFFI.forceGc();
|
||||
}
|
||||
|
||||
@@ -218,6 +218,16 @@ class ClashCore {
|
||||
clashFFI.stopTun();
|
||||
}
|
||||
|
||||
void setProcessMap(ProcessMapItem processMapItem) {
|
||||
clashFFI.setProcessMap(json.encode(processMapItem).toNativeUtf8().cast());
|
||||
}
|
||||
|
||||
DateTime? getRunTime() {
|
||||
final runTimeString = clashFFI.getRunTime().cast<Utf8>().toDartString();
|
||||
if (runTimeString.isEmpty) return null;
|
||||
return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
|
||||
}
|
||||
|
||||
List<Connection> getConnections() {
|
||||
final connectionsDataRaw = clashFFI.getConnections();
|
||||
final connectionsData =
|
||||
|
||||
@@ -1130,6 +1130,20 @@ class ClashFFI {
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function()>>('stopLog');
|
||||
late final _stopLog = _stopLogPtr.asFunction<void Function()>();
|
||||
|
||||
void setProcessMap(
|
||||
ffi.Pointer<ffi.Char> s,
|
||||
) {
|
||||
return _setProcessMap(
|
||||
s,
|
||||
);
|
||||
}
|
||||
|
||||
late final _setProcessMapPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(
|
||||
'setProcessMap');
|
||||
late final _setProcessMap =
|
||||
_setProcessMapPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
|
||||
|
||||
void startTUN(
|
||||
int fd,
|
||||
) {
|
||||
@@ -1142,19 +1156,15 @@ class ClashFFI {
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>('startTUN');
|
||||
late final _startTUN = _startTUNPtr.asFunction<void Function(int)>();
|
||||
|
||||
int updateMarkSocketPort(
|
||||
int markSocketPort,
|
||||
) {
|
||||
return _updateMarkSocketPort(
|
||||
markSocketPort,
|
||||
);
|
||||
ffi.Pointer<ffi.Char> getRunTime() {
|
||||
return _getRunTime();
|
||||
}
|
||||
|
||||
late final _updateMarkSocketPortPtr =
|
||||
_lookup<ffi.NativeFunction<GoUint8 Function(ffi.LongLong)>>(
|
||||
'updateMarkSocketPort');
|
||||
late final _updateMarkSocketPort =
|
||||
_updateMarkSocketPortPtr.asFunction<int Function(int)>();
|
||||
late final _getRunTimePtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(
|
||||
'getRunTime');
|
||||
late final _getRunTime =
|
||||
_getRunTimePtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
|
||||
|
||||
void stopTun() {
|
||||
return _stopTun();
|
||||
|
||||
@@ -14,7 +14,9 @@ abstract mixin class ClashMessageListener {
|
||||
|
||||
void onDelay(Delay delay) {}
|
||||
|
||||
void onProcess(Metadata metadata) {}
|
||||
void onProcess(Process process) {}
|
||||
|
||||
void onRequest(Connection connection) {}
|
||||
|
||||
void onNow(Now now) {}
|
||||
}
|
||||
@@ -41,11 +43,14 @@ class ClashMessage {
|
||||
listener.onDelay(Delay.fromJson(m.data));
|
||||
break;
|
||||
case MessageType.process:
|
||||
listener.onProcess(Metadata.fromJson(m.data));
|
||||
listener.onProcess(Process.fromJson(m.data));
|
||||
break;
|
||||
case MessageType.now:
|
||||
listener.onNow(Now.fromJson(m.data));
|
||||
break;
|
||||
case MessageType.request:
|
||||
listener.onRequest(Connection.fromJson(m.data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ class Android {
|
||||
init() async {
|
||||
app?.onExit = () {
|
||||
clashCore.shutdown();
|
||||
print("adsadda==>");
|
||||
exit(0);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ class Navigation {
|
||||
label: "profiles",
|
||||
fragment: ProfilesFragment(),
|
||||
),
|
||||
const NavigationItem(
|
||||
icon: Icon(Icons.ballot),
|
||||
label: "requests",
|
||||
fragment: RequestFragment(),
|
||||
description: "requestsDesc",
|
||||
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
|
||||
),
|
||||
const NavigationItem(
|
||||
icon: Icon(Icons.swap_vert_circle),
|
||||
label: "resources",
|
||||
|
||||
@@ -145,7 +145,16 @@ class AppController {
|
||||
if (isNotNeedUpdate == false || profile.type == ProfileType.file) {
|
||||
continue;
|
||||
}
|
||||
await updateProfile(profile.id);
|
||||
try {
|
||||
await updateProfile(profile.id);
|
||||
} catch (e) {
|
||||
appState.addLog(
|
||||
Log(
|
||||
logLevel: LogLevel.info,
|
||||
payload: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,20 +231,21 @@ class AppController {
|
||||
final tagName = data['tag_name'];
|
||||
final body = data['body'];
|
||||
final submits = other.parseReleaseBody(body);
|
||||
final textTheme = context.textTheme;
|
||||
globalState.showMessage(
|
||||
title: appLocalizations.discoverNewVersion,
|
||||
message: TextSpan(
|
||||
text: "$tagName \n",
|
||||
style: context.textTheme.headlineSmall,
|
||||
style: textTheme.headlineSmall,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "\n",
|
||||
style: context.textTheme.bodyMedium,
|
||||
style: textTheme.bodyMedium,
|
||||
),
|
||||
for (final submit in submits)
|
||||
TextSpan(
|
||||
text: "- $submit \n",
|
||||
style: context.textTheme.bodyMedium,
|
||||
style: textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -261,7 +271,7 @@ class AppController {
|
||||
window?.show();
|
||||
}
|
||||
final commonScaffoldState = globalState.homeScaffoldKey.currentState;
|
||||
if(commonScaffoldState?.mounted == true){
|
||||
if (commonScaffoldState?.mounted == true) {
|
||||
await commonScaffoldState?.loadingRun(() async {
|
||||
await globalState.applyProfile(
|
||||
appState: appState,
|
||||
@@ -269,7 +279,7 @@ class AppController {
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
});
|
||||
}else{
|
||||
} else {
|
||||
await globalState.applyProfile(
|
||||
appState: appState,
|
||||
config: config,
|
||||
|
||||
@@ -56,7 +56,9 @@ enum ProfileType { file, url }
|
||||
|
||||
enum ResultType { success, error }
|
||||
|
||||
enum MessageType { log, tun, delay, process, now }
|
||||
enum MessageType { log, tun, delay, process, now, request }
|
||||
|
||||
enum FindProcessMode { always, off }
|
||||
|
||||
enum RecoveryOption {
|
||||
all,
|
||||
|
||||
@@ -39,13 +39,6 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
}
|
||||
}
|
||||
|
||||
_updateLoglevel(LogLevel? logLevel) {
|
||||
if (logLevel == null ||
|
||||
logLevel == globalState.appController.clashConfig.logLevel) return;
|
||||
globalState.appController.clashConfig.logLevel = logLevel;
|
||||
globalState.appController.updateClashConfigDebounce();
|
||||
}
|
||||
|
||||
_buildAppSection() {
|
||||
final items = [
|
||||
if (Platform.isAndroid)
|
||||
@@ -121,34 +114,56 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
);
|
||||
}
|
||||
|
||||
_showLogLevelDialog(LogLevel value) {
|
||||
globalState.showCommonDialog(
|
||||
child: AlertDialog(
|
||||
title: Text(appLocalizations.logLevel),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 16,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: 250,
|
||||
child: Wrap(
|
||||
children: [
|
||||
for (final logLevel in LogLevel.values)
|
||||
ListItem.radio(
|
||||
delegate: RadioDelegate<LogLevel>(
|
||||
value: logLevel,
|
||||
groupValue: value,
|
||||
onChanged: (LogLevel? value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
final appController = globalState.appController;
|
||||
appController.clashConfig.logLevel = value;
|
||||
appController.updateClashConfigDebounce();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
title: Text(logLevel.name),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildGeneralSection() {
|
||||
final items = [
|
||||
Padding(
|
||||
padding: kMaterialListPadding,
|
||||
child: Selector<ClashConfig, LogLevel>(
|
||||
selector: (_, clashConfig) => clashConfig.logLevel,
|
||||
builder: (_, value, __) {
|
||||
return ListItem(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: Text(appLocalizations.logLevel),
|
||||
trailing: SizedBox(
|
||||
height: 48,
|
||||
child: DropdownMenu<LogLevel>(
|
||||
width: 124,
|
||||
initialSelection: value,
|
||||
dropdownMenuEntries: [
|
||||
for (final logLevel in LogLevel.values)
|
||||
DropdownMenuEntry<LogLevel>(
|
||||
value: logLevel,
|
||||
label: logLevel.name,
|
||||
)
|
||||
],
|
||||
onSelected: _updateLoglevel,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<ClashConfig, LogLevel>(
|
||||
selector: (_, clashConfig) => clashConfig.logLevel,
|
||||
builder: (_, value, __) {
|
||||
return ListItem(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: Text(appLocalizations.logLevel),
|
||||
subtitle: Text(value.name),
|
||||
onTab: () {
|
||||
_showLogLevelDialog(value);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<ClashConfig, int>(
|
||||
selector: (_, clashConfig) => clashConfig.mixedPort,
|
||||
@@ -225,6 +240,26 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<ClashConfig, bool>(
|
||||
selector: (_, clashConfig) =>
|
||||
clashConfig.findProcessMode == FindProcessMode.always,
|
||||
builder: (_, findProcess, __) {
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.polymer_outlined),
|
||||
title: Text(appLocalizations.findProcessMode),
|
||||
subtitle: Text(appLocalizations.findProcessModeDesc),
|
||||
delegate: SwitchDelegate(
|
||||
value: findProcess,
|
||||
onChanged: (bool value) async {
|
||||
final appController = globalState.appController;
|
||||
appController.clashConfig.findProcessMode =
|
||||
value ? FindProcessMode.always : FindProcessMode.off;
|
||||
appController.updateClashConfigDebounce();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Selector<ClashConfig, bool>(
|
||||
selector: (_, clashConfig) => clashConfig.tcpConcurrent,
|
||||
builder: (_, tcpConcurrent, __) {
|
||||
@@ -258,7 +293,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
appController.clashConfig.geodataLoader = value
|
||||
? geodataLoaderMemconservative
|
||||
: geodataLoaderStandard;
|
||||
appController.updateClashConfigDebounce;
|
||||
appController.updateClashConfigDebounce();
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -277,7 +312,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
final appController = globalState.appController;
|
||||
appController.clashConfig.externalController =
|
||||
value ? defaultExternalController : '';
|
||||
appController.updateClashConfigDebounce;
|
||||
appController.updateClashConfigDebounce();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,140 +1,140 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/clash/core.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ConnectionsFragment extends StatefulWidget {
|
||||
const ConnectionsFragment({super.key});
|
||||
|
||||
@override
|
||||
State<ConnectionsFragment> createState() => _ConnectionsFragmentState();
|
||||
}
|
||||
|
||||
class _ConnectionsFragmentState extends State<ConnectionsFragment> {
|
||||
final connectionsNotifier = ValueNotifier<List<Connection>>([]);
|
||||
Map<String, String?> idPackageNameMap = {};
|
||||
|
||||
Timer? timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_getConnections();
|
||||
if (timer != null) {
|
||||
timer?.cancel();
|
||||
timer = null;
|
||||
}
|
||||
timer = Timer.periodic(const Duration(seconds: 3), (timer) {
|
||||
if (mounted) {
|
||||
_getConnections();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_getConnections() {
|
||||
connectionsNotifier.value = clashCore
|
||||
.getConnections();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
timer?.cancel();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
Future<ImageProvider?> _getPackageIconWithConnection(
|
||||
Connection connection) async {
|
||||
final uid = connection.metadata.uid;
|
||||
// if(globalState.packageNameMap[uid] == null){
|
||||
// globalState.packageNameMap[uid] = await app?.getPackageName(connection.metadata);
|
||||
// }
|
||||
final packageName = globalState.packageNameMap[uid];
|
||||
if(packageName == null) return null;
|
||||
return await app?.getPackageIcon(packageName);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<List<Connection>>(
|
||||
valueListenable: connectionsNotifier,
|
||||
builder: (_, List<Connection> connections, __) {
|
||||
if (connections.isEmpty) {
|
||||
return const NullStatus(
|
||||
label: "未开启代理,或者没有连接数据",
|
||||
);
|
||||
}
|
||||
return ListView.separated(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
itemBuilder: (_, index) {
|
||||
final connection = connections[index];
|
||||
return ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.top,
|
||||
leading: Container(
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: FutureBuilder<ImageProvider?>(
|
||||
future: _getPackageIconWithConnection(connection),
|
||||
builder: (_, snapshot) {
|
||||
if (!snapshot.hasData && snapshot.data == null) {
|
||||
return Container();
|
||||
} else {
|
||||
return Image(
|
||||
image: snapshot.data!,
|
||||
gaplessPlayback: true,
|
||||
width: 48,
|
||||
height: 48,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(connection.metadata.host.isNotEmpty
|
||||
? connection.metadata.host
|
||||
: connection.metadata.destinationIP),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (final chain in connection.chains)
|
||||
CommonChip(
|
||||
label: chain,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.block),
|
||||
onPressed: () {},
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const Divider(
|
||||
height: 0,
|
||||
);
|
||||
},
|
||||
itemCount: connections.length,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
// import 'dart:async';
|
||||
//
|
||||
// import 'package:fl_clash/clash/core.dart';
|
||||
// import 'package:fl_clash/models/models.dart';
|
||||
// import 'package:fl_clash/plugins/app.dart';
|
||||
// import 'package:fl_clash/state.dart';
|
||||
// import 'package:fl_clash/widgets/widgets.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
//
|
||||
// class ConnectionsFragment extends StatefulWidget {
|
||||
// const ConnectionsFragment({super.key});
|
||||
//
|
||||
// @override
|
||||
// State<ConnectionsFragment> createState() => _ConnectionsFragmentState();
|
||||
// }
|
||||
//
|
||||
// class _ConnectionsFragmentState extends State<ConnectionsFragment> {
|
||||
// final connectionsNotifier = ValueNotifier<List<Connection>>([]);
|
||||
// Map<String, String?> idPackageNameMap = {};
|
||||
//
|
||||
// Timer? timer;
|
||||
//
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
// _getConnections();
|
||||
// if (timer != null) {
|
||||
// timer?.cancel();
|
||||
// timer = null;
|
||||
// }
|
||||
// timer = Timer.periodic(const Duration(seconds: 3), (timer) {
|
||||
// if (mounted) {
|
||||
// _getConnections();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// _getConnections() {
|
||||
// connectionsNotifier.value = clashCore
|
||||
// .getConnections();
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// void dispose() {
|
||||
// super.dispose();
|
||||
// timer?.cancel();
|
||||
// timer = null;
|
||||
// }
|
||||
//
|
||||
// Future<ImageProvider?> _getPackageIconWithConnection(
|
||||
// Connection connection) async {
|
||||
// final uid = connection.metadata.uid;
|
||||
// // if(globalState.packageNameMap[uid] == null){
|
||||
// // globalState.packageNameMap[uid] = await app?.getPackageName(connection.metadata);
|
||||
// // }
|
||||
// final packageName = globalState.packageNameMap[uid];
|
||||
// if(packageName == null) return null;
|
||||
// return await app?.getPackageIcon(packageName);
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return ValueListenableBuilder<List<Connection>>(
|
||||
// valueListenable: connectionsNotifier,
|
||||
// builder: (_, List<Connection> connections, __) {
|
||||
// if (connections.isEmpty) {
|
||||
// return const NullStatus(
|
||||
// label: "未开启代理,或者没有连接数据",
|
||||
// );
|
||||
// }
|
||||
// return ListView.separated(
|
||||
// physics: const AlwaysScrollableScrollPhysics(),
|
||||
// itemBuilder: (_, index) {
|
||||
// final connection = connections[index];
|
||||
// return ListTile(
|
||||
// titleAlignment: ListTileTitleAlignment.top,
|
||||
// leading: Container(
|
||||
// margin: const EdgeInsets.only(top: 4),
|
||||
// width: 48,
|
||||
// height: 48,
|
||||
// child: FutureBuilder<ImageProvider?>(
|
||||
// future: _getPackageIconWithConnection(connection),
|
||||
// builder: (_, snapshot) {
|
||||
// if (!snapshot.hasData && snapshot.data == null) {
|
||||
// return Container();
|
||||
// } else {
|
||||
// return Image(
|
||||
// image: snapshot.data!,
|
||||
// gaplessPlayback: true,
|
||||
// width: 48,
|
||||
// height: 48,
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// contentPadding:
|
||||
// const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
// title: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(connection.metadata.host.isNotEmpty
|
||||
// ? connection.metadata.host
|
||||
// : connection.metadata.destinationIP),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(
|
||||
// top: 12,
|
||||
// ),
|
||||
// child: Wrap(
|
||||
// runSpacing: 8,
|
||||
// spacing: 8,
|
||||
// children: [
|
||||
// for (final chain in connection.chains)
|
||||
// CommonChip(
|
||||
// label: chain,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// trailing: IconButton(
|
||||
// icon: const Icon(Icons.block),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// separatorBuilder: (BuildContext context, int index) {
|
||||
// return const Divider(
|
||||
// height: 0,
|
||||
// );
|
||||
// },
|
||||
// itemCount: connections.length,
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -9,4 +9,5 @@ export 'config.dart';
|
||||
export 'application_setting.dart';
|
||||
export 'about.dart';
|
||||
export 'backup_and_recovery.dart';
|
||||
export 'resources.dart';
|
||||
export 'resources.dart';
|
||||
export 'requests.dart';
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
@@ -22,14 +23,21 @@ class _LogsFragmentState extends State<LogsFragment> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
logsNotifier.value = context.read<AppState>().logs;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final appState = globalState.appController.appState;
|
||||
logsNotifier.value = List<Log>.from(appState.logs);
|
||||
if (timer != null) {
|
||||
timer?.cancel();
|
||||
timer = null;
|
||||
}
|
||||
timer = Timer.periodic(const Duration(milliseconds: 200), (timer) {
|
||||
logsNotifier.value = globalState.appController.appState.logs;
|
||||
final logs = List<Log>.from(appState.logs);
|
||||
if (!const ListEquality<Log>().equals(
|
||||
logsNotifier.value,
|
||||
logs,
|
||||
)) {
|
||||
logsNotifier.value = logs;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -59,6 +60,18 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
|
||||
});
|
||||
}
|
||||
|
||||
_handleTabControllerChange() {
|
||||
final indexIsChanging = _tabController?.indexIsChanging ?? false;
|
||||
if (indexIsChanging) return;
|
||||
final index = _tabController?.index;
|
||||
if(index == null) return;
|
||||
final appController = globalState.appController;
|
||||
final currentGroups = appController.appState.currentGroups;
|
||||
if (currentGroups.length > index) {
|
||||
appController.config.updateCurrentGroupName(currentGroups[index].name);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<AppState, bool>(
|
||||
@@ -69,26 +82,34 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>(
|
||||
selector: (_, appState, config, clashConfig) {
|
||||
child: Selector2<AppState, Config, ProxiesSelectorState>(
|
||||
selector: (_, appState, config) {
|
||||
final currentGroups = appState.currentGroups;
|
||||
final groupNames = currentGroups.map((e) => e.name).toList();
|
||||
return ProxiesSelectorState(
|
||||
groupNames: groupNames,
|
||||
currentGroupName: config.currentGroupName,
|
||||
);
|
||||
},
|
||||
shouldRebuild: (prev, next) {
|
||||
if (prev.groupNames.length != next.groupNames.length) {
|
||||
if (!const ListEquality<String>()
|
||||
.equals(prev.groupNames, next.groupNames)) {
|
||||
_tabController?.removeListener(_handleTabControllerChange);
|
||||
_tabController?.dispose();
|
||||
_tabController = null;
|
||||
return true;
|
||||
}
|
||||
return prev != next;
|
||||
return false;
|
||||
},
|
||||
builder: (_, state, __) {
|
||||
final index = state.groupNames.indexWhere(
|
||||
(item) => item == state.currentGroupName,
|
||||
);
|
||||
_tabController ??= TabController(
|
||||
length: state.groupNames.length,
|
||||
initialIndex: index == -1 ? 0 : index,
|
||||
vsync: this,
|
||||
);
|
||||
)..addListener(_handleTabControllerChange);
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -200,7 +221,8 @@ class ProxiesTabView extends StatelessWidget {
|
||||
_delayTest(List<Proxy> proxies) async {
|
||||
for (final proxy in proxies) {
|
||||
final appController = globalState.appController;
|
||||
final proxyName = appController.appState.getRealProxyName(proxy.name) ?? proxy.name;
|
||||
final proxyName =
|
||||
appController.appState.getRealProxyName(proxy.name) ?? proxy.name;
|
||||
globalState.appController.setDelay(
|
||||
Delay(
|
||||
name: proxyName,
|
||||
|
||||
178
lib/fragments/requests.dart
Normal file
178
lib/fragments/requests.dart
Normal file
@@ -0,0 +1,178 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RequestFragment extends StatefulWidget {
|
||||
const RequestFragment({super.key});
|
||||
|
||||
@override
|
||||
State<RequestFragment> createState() => _RequestFragmentState();
|
||||
}
|
||||
|
||||
class _RequestFragmentState extends State<RequestFragment> {
|
||||
final requestsNotifier = ValueNotifier<List<Connection>>([]);
|
||||
final ScrollController _scrollController = ScrollController(
|
||||
keepScrollOffset: false,
|
||||
);
|
||||
|
||||
Timer? timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final appState = globalState.appController.appState;
|
||||
requestsNotifier.value = List<Connection>.from(appState.requests);
|
||||
if (timer != null) {
|
||||
timer?.cancel();
|
||||
timer = null;
|
||||
}
|
||||
timer = Timer.periodic(const Duration(milliseconds: 200), (timer) {
|
||||
final requests = List<Connection>.from(appState.requests);
|
||||
if (!const ListEquality<Connection>().equals(
|
||||
requestsNotifier.value,
|
||||
requests,
|
||||
)) {
|
||||
requestsNotifier.value = requests;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
timer?.cancel();
|
||||
_scrollController.dispose();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<List<Connection>>(
|
||||
valueListenable: requestsNotifier,
|
||||
builder: (_, List<Connection> connections, __) {
|
||||
if (connections.isEmpty) {
|
||||
return NullStatus(
|
||||
label: appLocalizations.nullRequestsDesc,
|
||||
);
|
||||
}
|
||||
connections = connections.reversed.toList();
|
||||
return ListView.separated(
|
||||
controller: _scrollController,
|
||||
itemBuilder: (_, index) {
|
||||
final connection = connections[index];
|
||||
return RequestItem(
|
||||
key: Key(connection.id),
|
||||
connection: connection,
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return const Divider(
|
||||
height: 0,
|
||||
);
|
||||
},
|
||||
itemCount: connections.length,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RequestItem extends StatelessWidget {
|
||||
final Connection connection;
|
||||
|
||||
const RequestItem({
|
||||
super.key,
|
||||
required this.connection,
|
||||
});
|
||||
|
||||
Future<ImageProvider?> _getPackageIcon(Connection connection) async {
|
||||
return await app?.getPackageIcon(connection.metadata.process);
|
||||
}
|
||||
|
||||
String _getRequestText(Metadata metadata) {
|
||||
var text = "${metadata.network}:://";
|
||||
final ips = [
|
||||
metadata.host,
|
||||
metadata.destinationIP,
|
||||
].where((ip) => ip.isNotEmpty);
|
||||
text += ips.join("/");
|
||||
text += ":${metadata.destinationPort}";
|
||||
return text;
|
||||
}
|
||||
|
||||
String _getSourceText(Connection connection) {
|
||||
final metadata = connection.metadata;
|
||||
if (metadata.process.isEmpty) {
|
||||
return connection.start.lastUpdateTimeDesc;
|
||||
}
|
||||
return "${metadata.process} · ${connection.start.lastUpdateTimeDesc}";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.top,
|
||||
leading: Platform.isAndroid
|
||||
? Container(
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: FutureBuilder<ImageProvider?>(
|
||||
future: _getPackageIcon(connection),
|
||||
builder: (_, snapshot) {
|
||||
if (!snapshot.hasData && snapshot.data == null) {
|
||||
return Container();
|
||||
} else {
|
||||
return Image(
|
||||
image: snapshot.data!,
|
||||
gaplessPlayback: true,
|
||||
width: 48,
|
||||
height: 48,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
: null,
|
||||
title: Text(
|
||||
_getRequestText(connection.metadata),
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Text(
|
||||
_getSourceText(connection),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (final chain in connection.chains)
|
||||
CommonChip(
|
||||
label: chain,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -176,5 +176,10 @@
|
||||
"tcpConcurrent": "Tcp concurrent",
|
||||
"tcpConcurrentDesc": "Enabling it will allow tcp concurrency",
|
||||
"geodataLoader": "Geo Low Memory Mode",
|
||||
"geodataLoaderDesc": "Enabling will use the Geo low memory loader"
|
||||
"geodataLoaderDesc": "Enabling will use the Geo low memory loader",
|
||||
"requests": "Requests",
|
||||
"requestsDesc": "View recently requested data",
|
||||
"nullRequestsDesc": "No proxy or no request",
|
||||
"findProcessMode": "Find process",
|
||||
"findProcessModeDesc": "There is a risk of flashback after opening"
|
||||
}
|
||||
@@ -176,5 +176,10 @@
|
||||
"tcpConcurrent": "TCP并发",
|
||||
"tcpConcurrentDesc": "开启后允许tcp并发",
|
||||
"geodataLoader": "Geo低内存模式",
|
||||
"geodataLoaderDesc": "开启将使用Geo低内存加载器"
|
||||
"geodataLoaderDesc": "开启将使用Geo低内存加载器",
|
||||
"requests": "请求",
|
||||
"requestsDesc": "查看最近请求数据",
|
||||
"nullRequestsDesc": "未开启代理或者没有请求",
|
||||
"findProcessMode": "查找进程",
|
||||
"findProcessModeDesc": "开启后存在闪退风险"
|
||||
}
|
||||
@@ -128,6 +128,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Directly upload profile"),
|
||||
"filterSystemApp":
|
||||
MessageLookupByLibrary.simpleMessage("Filter system app"),
|
||||
"findProcessMode": MessageLookupByLibrary.simpleMessage("Find process"),
|
||||
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"There is a risk of flashback after opening"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("General"),
|
||||
"geoData": MessageLookupByLibrary.simpleMessage("GeoData"),
|
||||
"geodataLoader":
|
||||
@@ -176,6 +179,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"nullLogsDesc": MessageLookupByLibrary.simpleMessage("No logs"),
|
||||
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"No profile, Please add a profile"),
|
||||
"nullRequestsDesc":
|
||||
MessageLookupByLibrary.simpleMessage("No proxy or no request"),
|
||||
"other": MessageLookupByLibrary.simpleMessage("Other"),
|
||||
"outboundMode": MessageLookupByLibrary.simpleMessage("Outbound mode"),
|
||||
"override": MessageLookupByLibrary.simpleMessage("Override"),
|
||||
@@ -225,6 +230,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Only recovery profiles"),
|
||||
"recoverySuccess":
|
||||
MessageLookupByLibrary.simpleMessage("Recovery success"),
|
||||
"requests": MessageLookupByLibrary.simpleMessage("Requests"),
|
||||
"requestsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"View recently requested data"),
|
||||
"resources": MessageLookupByLibrary.simpleMessage("Resources"),
|
||||
"resourcesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"External resource related info"),
|
||||
|
||||
@@ -104,6 +104,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"file": MessageLookupByLibrary.simpleMessage("文件"),
|
||||
"fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"),
|
||||
"filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"),
|
||||
"findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"),
|
||||
"findProcessModeDesc":
|
||||
MessageLookupByLibrary.simpleMessage("开启后存在闪退风险"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("基础"),
|
||||
"geoData": MessageLookupByLibrary.simpleMessage("地理数据"),
|
||||
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"),
|
||||
@@ -143,6 +146,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"nullLogsDesc": MessageLookupByLibrary.simpleMessage("暂无日志"),
|
||||
"nullProfileDesc":
|
||||
MessageLookupByLibrary.simpleMessage("没有配置文件,请先添加配置文件"),
|
||||
"nullRequestsDesc": MessageLookupByLibrary.simpleMessage("未开启代理或者没有请求"),
|
||||
"other": MessageLookupByLibrary.simpleMessage("其他"),
|
||||
"outboundMode": MessageLookupByLibrary.simpleMessage("出站模式"),
|
||||
"override": MessageLookupByLibrary.simpleMessage("覆写"),
|
||||
@@ -180,6 +184,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"recoveryDesc": MessageLookupByLibrary.simpleMessage("从WebDAV恢复数据"),
|
||||
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"),
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"),
|
||||
"requests": MessageLookupByLibrary.simpleMessage("请求"),
|
||||
"requestsDesc": MessageLookupByLibrary.simpleMessage("查看最近请求数据"),
|
||||
"resources": MessageLookupByLibrary.simpleMessage("资源"),
|
||||
"resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"),
|
||||
"rule": MessageLookupByLibrary.simpleMessage("规则"),
|
||||
|
||||
@@ -1829,6 +1829,56 @@ class AppLocalizations {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Requests`
|
||||
String get requests {
|
||||
return Intl.message(
|
||||
'Requests',
|
||||
name: 'requests',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `View recently requested data`
|
||||
String get requestsDesc {
|
||||
return Intl.message(
|
||||
'View recently requested data',
|
||||
name: 'requestsDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `No proxy or no request`
|
||||
String get nullRequestsDesc {
|
||||
return Intl.message(
|
||||
'No proxy or no request',
|
||||
name: 'nullRequestsDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Find process`
|
||||
String get findProcessMode {
|
||||
return Intl.message(
|
||||
'Find process',
|
||||
name: 'findProcessMode',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `There is a risk of flashback after opening`
|
||||
String get findProcessModeDesc {
|
||||
return Intl.message(
|
||||
'There is a risk of flashback after opening',
|
||||
name: 'findProcessModeDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -31,7 +31,6 @@ Future<void> main() async {
|
||||
config: config,
|
||||
clashConfig: clashConfig,
|
||||
);
|
||||
|
||||
runAppWithPreferences(
|
||||
const Application(),
|
||||
appState: appState,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'connection.dart';
|
||||
import 'ffi.dart';
|
||||
import 'log.dart';
|
||||
import 'navigation.dart';
|
||||
@@ -29,6 +32,7 @@ class AppState with ChangeNotifier {
|
||||
bool _isCompatible;
|
||||
List<Group> _groups;
|
||||
double _viewWidth;
|
||||
List<Connection> _requests;
|
||||
|
||||
AppState({
|
||||
required Mode mode,
|
||||
@@ -42,6 +46,7 @@ class AppState with ChangeNotifier {
|
||||
_viewWidth = 0,
|
||||
_selectedMap = selectedMap,
|
||||
_sortNum = 0,
|
||||
_requests = [],
|
||||
_mode = mode,
|
||||
_delayMap = {},
|
||||
_groups = [],
|
||||
@@ -157,6 +162,24 @@ class AppState with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<Connection> get requests => _requests;
|
||||
|
||||
set requests(List<Connection> value) {
|
||||
if (_requests != value) {
|
||||
_requests = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
addRequest(Connection value) {
|
||||
_requests.add(value);
|
||||
final maxLength = Platform.isAndroid ? 1000 : 60;
|
||||
if (_requests.length > maxLength) {
|
||||
_requests = _requests.sublist(_requests.length - maxLength);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<Log> get logs => _logs;
|
||||
|
||||
set logs(List<Log> value) {
|
||||
@@ -168,8 +191,10 @@ class AppState with ChangeNotifier {
|
||||
|
||||
addLog(Log log) {
|
||||
_logs.add(log);
|
||||
if (_logs.length > 60) {
|
||||
_logs = _logs.sublist(_logs.length - 60);
|
||||
if (!Platform.isAndroid) {
|
||||
if (_logs.length > 60) {
|
||||
_logs = _logs.sublist(_logs.length - 60);
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -113,39 +113,29 @@ class ClashConfig extends ChangeNotifier {
|
||||
LogLevel _logLevel;
|
||||
String _externalController;
|
||||
Mode _mode;
|
||||
FindProcessMode _findProcessMode;
|
||||
bool _unifiedDelay;
|
||||
bool _tcpConcurrent;
|
||||
Tun _tun;
|
||||
Dns _dns;
|
||||
List<String> _rules;
|
||||
|
||||
ClashConfig({
|
||||
int? mixedPort,
|
||||
Mode? mode,
|
||||
bool? allowLan,
|
||||
bool? ipv6,
|
||||
LogLevel? logLevel,
|
||||
String? externalController,
|
||||
String? geodataLoader,
|
||||
bool? unifiedDelay,
|
||||
Tun? tun,
|
||||
Dns? dns,
|
||||
bool? tcpConcurrent,
|
||||
List<String>? rules,
|
||||
}) : _mixedPort = mixedPort ?? 7890,
|
||||
_mode = mode ?? Mode.rule,
|
||||
_ipv6 = ipv6 ?? false,
|
||||
_allowLan = allowLan ?? false,
|
||||
_tcpConcurrent = tcpConcurrent ?? false,
|
||||
_logLevel = logLevel ?? LogLevel.info,
|
||||
_tun = tun ?? const Tun(),
|
||||
_unifiedDelay = unifiedDelay ?? false,
|
||||
_geodataLoader = geodataLoader ?? geodataLoaderMemconservative,
|
||||
_externalController = externalController ?? '',
|
||||
_dns = dns ?? Dns(),
|
||||
_rules = rules ?? [];
|
||||
ClashConfig()
|
||||
: _mixedPort = 7890,
|
||||
_mode = Mode.rule,
|
||||
_ipv6 = false,
|
||||
_findProcessMode = FindProcessMode.off,
|
||||
_allowLan = false,
|
||||
_tcpConcurrent = false,
|
||||
_logLevel = LogLevel.info,
|
||||
_tun = const Tun(),
|
||||
_unifiedDelay = false,
|
||||
_geodataLoader = geodataLoaderMemconservative,
|
||||
_externalController = '',
|
||||
_dns = Dns(),
|
||||
_rules = [];
|
||||
|
||||
@JsonKey(name: "mixed-port")
|
||||
@JsonKey(name: "mixed-port", defaultValue: 7890)
|
||||
int get mixedPort => _mixedPort;
|
||||
|
||||
set mixedPort(int value) {
|
||||
@@ -155,6 +145,7 @@ class ClashConfig extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonKey(defaultValue: Mode.rule)
|
||||
Mode get mode => _mode;
|
||||
|
||||
set mode(Mode value) {
|
||||
@@ -164,6 +155,16 @@ class ClashConfig extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonKey(name: "find-process-mode", defaultValue: FindProcessMode.off)
|
||||
FindProcessMode get findProcessMode => _findProcessMode;
|
||||
|
||||
set findProcessMode(FindProcessMode value) {
|
||||
if (_findProcessMode != value) {
|
||||
_findProcessMode = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@JsonKey(name: "allow-lan")
|
||||
bool get allowLan => _allowLan;
|
||||
|
||||
@@ -174,7 +175,7 @@ class ClashConfig extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
@JsonKey(name: "log-level")
|
||||
@JsonKey(name: "log-level", defaultValue: LogLevel.info)
|
||||
LogLevel get logLevel => _logLevel;
|
||||
|
||||
set logLevel(LogLevel value) {
|
||||
@@ -282,17 +283,6 @@ class ClashConfig extends ChangeNotifier {
|
||||
return _$ClashConfigFromJson(json);
|
||||
}
|
||||
|
||||
ClashConfig copyWith({Tun? tun}) {
|
||||
return ClashConfig(
|
||||
mixedPort: mixedPort,
|
||||
mode: mode,
|
||||
logLevel: logLevel,
|
||||
tun: tun ?? this.tun,
|
||||
dns: dns,
|
||||
allowLan: allowLan,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ClashConfig{_mixedPort: $_mixedPort, _allowLan: $_allowLan, _mode: $_mode, _logLevel: $_logLevel, _tun: $_tun, _dns: $_dns, _rules: $_rules}';
|
||||
|
||||
@@ -32,8 +32,7 @@ class Props with _$Props {
|
||||
bool? systemProxy,
|
||||
}) = _Props;
|
||||
|
||||
factory Props.fromJson(Map<String, Object?> json) =>
|
||||
_$PropsFromJson(json);
|
||||
factory Props.fromJson(Map<String, Object?> json) => _$PropsFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
@@ -151,6 +150,15 @@ class Config extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
String? get currentGroupName => currentProfile?.currentGroupName;
|
||||
|
||||
updateCurrentGroupName(String groupName) {
|
||||
if (currentProfile?.currentGroupName != groupName) {
|
||||
currentProfile?.currentGroupName = groupName;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
SelectedMap get currentSelectedMap {
|
||||
return currentProfile?.selectedMap ?? {};
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ class Metadata with _$Metadata {
|
||||
required String destinationIP,
|
||||
required String destinationPort,
|
||||
required String host,
|
||||
required String process,
|
||||
required String remoteDestination,
|
||||
}) = _Metadata;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/clash_config.dart';
|
||||
import 'package:fl_clash/models/connection.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'generated/ffi.g.dart';
|
||||
@@ -66,16 +67,25 @@ class Now with _$Now {
|
||||
@freezed
|
||||
class Process with _$Process {
|
||||
const factory Process({
|
||||
required int uid,
|
||||
required String network,
|
||||
required String source,
|
||||
required String target,
|
||||
required int id,
|
||||
required Metadata metadata,
|
||||
}) = _Process;
|
||||
|
||||
factory Process.fromJson(Map<String, Object?> json) =>
|
||||
_$ProcessFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProcessMapItem with _$ProcessMapItem {
|
||||
const factory ProcessMapItem({
|
||||
required int id,
|
||||
String? value,
|
||||
}) = _ProcessMapItem;
|
||||
|
||||
factory ProcessMapItem.fromJson(Map<String, Object?> json) =>
|
||||
_$ProcessMapItemFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ExternalProvider with _$ExternalProvider {
|
||||
const factory ExternalProvider({
|
||||
|
||||
@@ -35,30 +35,29 @@ Map<String, dynamic> _$DnsToJson(Dns instance) => <String, dynamic>{
|
||||
'fake-ip-filter': instance.fakeIpFilter,
|
||||
};
|
||||
|
||||
ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => ClashConfig(
|
||||
mixedPort: (json['mixed-port'] as num?)?.toInt(),
|
||||
mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']),
|
||||
allowLan: json['allow-lan'] as bool?,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['log-level']),
|
||||
externalController: json['external-controller'] as String? ?? '',
|
||||
geodataLoader: json['geodata-loader'] as String? ?? 'memconservative',
|
||||
unifiedDelay: json['unified-delay'] as bool? ?? false,
|
||||
tun: json['tun'] == null
|
||||
? null
|
||||
: Tun.fromJson(json['tun'] as Map<String, dynamic>),
|
||||
dns: json['dns'] == null
|
||||
? null
|
||||
: Dns.fromJson(json['dns'] as Map<String, dynamic>),
|
||||
tcpConcurrent: json['tcp-concurrent'] as bool? ?? false,
|
||||
rules:
|
||||
(json['rules'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||
);
|
||||
ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => ClashConfig()
|
||||
..mixedPort = (json['mixed-port'] as num?)?.toInt() ?? 7890
|
||||
..mode = $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule
|
||||
..findProcessMode = $enumDecodeNullable(
|
||||
_$FindProcessModeEnumMap, json['find-process-mode']) ??
|
||||
FindProcessMode.off
|
||||
..allowLan = json['allow-lan'] as bool
|
||||
..logLevel =
|
||||
$enumDecodeNullable(_$LogLevelEnumMap, json['log-level']) ?? LogLevel.info
|
||||
..externalController = json['external-controller'] as String? ?? ''
|
||||
..ipv6 = json['ipv6'] as bool? ?? false
|
||||
..geodataLoader = json['geodata-loader'] as String? ?? 'memconservative'
|
||||
..unifiedDelay = json['unified-delay'] as bool? ?? false
|
||||
..tcpConcurrent = json['tcp-concurrent'] as bool? ?? false
|
||||
..tun = Tun.fromJson(json['tun'] as Map<String, dynamic>)
|
||||
..dns = Dns.fromJson(json['dns'] as Map<String, dynamic>)
|
||||
..rules = (json['rules'] as List<dynamic>).map((e) => e as String).toList();
|
||||
|
||||
Map<String, dynamic> _$ClashConfigToJson(ClashConfig instance) =>
|
||||
<String, dynamic>{
|
||||
'mixed-port': instance.mixedPort,
|
||||
'mode': _$ModeEnumMap[instance.mode]!,
|
||||
'find-process-mode': _$FindProcessModeEnumMap[instance.findProcessMode]!,
|
||||
'allow-lan': instance.allowLan,
|
||||
'log-level': _$LogLevelEnumMap[instance.logLevel]!,
|
||||
'external-controller': instance.externalController,
|
||||
@@ -77,6 +76,11 @@ const _$ModeEnumMap = {
|
||||
Mode.direct: 'direct',
|
||||
};
|
||||
|
||||
const _$FindProcessModeEnumMap = {
|
||||
FindProcessMode.always: 'always',
|
||||
FindProcessMode.off: 'off',
|
||||
};
|
||||
|
||||
const _$LogLevelEnumMap = {
|
||||
LogLevel.debug: 'debug',
|
||||
LogLevel.info: 'info',
|
||||
|
||||
@@ -27,6 +27,7 @@ mixin _$Metadata {
|
||||
String get destinationIP => throw _privateConstructorUsedError;
|
||||
String get destinationPort => throw _privateConstructorUsedError;
|
||||
String get host => throw _privateConstructorUsedError;
|
||||
String get process => throw _privateConstructorUsedError;
|
||||
String get remoteDestination => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -48,6 +49,7 @@ abstract class $MetadataCopyWith<$Res> {
|
||||
String destinationIP,
|
||||
String destinationPort,
|
||||
String host,
|
||||
String process,
|
||||
String remoteDestination});
|
||||
}
|
||||
|
||||
@@ -71,6 +73,7 @@ class _$MetadataCopyWithImpl<$Res, $Val extends Metadata>
|
||||
Object? destinationIP = null,
|
||||
Object? destinationPort = null,
|
||||
Object? host = null,
|
||||
Object? process = null,
|
||||
Object? remoteDestination = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@@ -102,6 +105,10 @@ class _$MetadataCopyWithImpl<$Res, $Val extends Metadata>
|
||||
? _value.host
|
||||
: host // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
process: null == process
|
||||
? _value.process
|
||||
: process // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
remoteDestination: null == remoteDestination
|
||||
? _value.remoteDestination
|
||||
: remoteDestination // ignore: cast_nullable_to_non_nullable
|
||||
@@ -126,6 +133,7 @@ abstract class _$$MetadataImplCopyWith<$Res>
|
||||
String destinationIP,
|
||||
String destinationPort,
|
||||
String host,
|
||||
String process,
|
||||
String remoteDestination});
|
||||
}
|
||||
|
||||
@@ -147,6 +155,7 @@ class __$$MetadataImplCopyWithImpl<$Res>
|
||||
Object? destinationIP = null,
|
||||
Object? destinationPort = null,
|
||||
Object? host = null,
|
||||
Object? process = null,
|
||||
Object? remoteDestination = null,
|
||||
}) {
|
||||
return _then(_$MetadataImpl(
|
||||
@@ -178,6 +187,10 @@ class __$$MetadataImplCopyWithImpl<$Res>
|
||||
? _value.host
|
||||
: host // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
process: null == process
|
||||
? _value.process
|
||||
: process // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
remoteDestination: null == remoteDestination
|
||||
? _value.remoteDestination
|
||||
: remoteDestination // ignore: cast_nullable_to_non_nullable
|
||||
@@ -197,6 +210,7 @@ class _$MetadataImpl implements _Metadata {
|
||||
required this.destinationIP,
|
||||
required this.destinationPort,
|
||||
required this.host,
|
||||
required this.process,
|
||||
required this.remoteDestination});
|
||||
|
||||
factory _$MetadataImpl.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -217,11 +231,13 @@ class _$MetadataImpl implements _Metadata {
|
||||
@override
|
||||
final String host;
|
||||
@override
|
||||
final String process;
|
||||
@override
|
||||
final String remoteDestination;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Metadata(uid: $uid, network: $network, sourceIP: $sourceIP, sourcePort: $sourcePort, destinationIP: $destinationIP, destinationPort: $destinationPort, host: $host, remoteDestination: $remoteDestination)';
|
||||
return 'Metadata(uid: $uid, network: $network, sourceIP: $sourceIP, sourcePort: $sourcePort, destinationIP: $destinationIP, destinationPort: $destinationPort, host: $host, process: $process, remoteDestination: $remoteDestination)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -240,14 +256,24 @@ class _$MetadataImpl implements _Metadata {
|
||||
(identical(other.destinationPort, destinationPort) ||
|
||||
other.destinationPort == destinationPort) &&
|
||||
(identical(other.host, host) || other.host == host) &&
|
||||
(identical(other.process, process) || other.process == process) &&
|
||||
(identical(other.remoteDestination, remoteDestination) ||
|
||||
other.remoteDestination == remoteDestination));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, uid, network, sourceIP,
|
||||
sourcePort, destinationIP, destinationPort, host, remoteDestination);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
uid,
|
||||
network,
|
||||
sourceIP,
|
||||
sourcePort,
|
||||
destinationIP,
|
||||
destinationPort,
|
||||
host,
|
||||
process,
|
||||
remoteDestination);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@@ -272,6 +298,7 @@ abstract class _Metadata implements Metadata {
|
||||
required final String destinationIP,
|
||||
required final String destinationPort,
|
||||
required final String host,
|
||||
required final String process,
|
||||
required final String remoteDestination}) = _$MetadataImpl;
|
||||
|
||||
factory _Metadata.fromJson(Map<String, dynamic> json) =
|
||||
@@ -292,6 +319,8 @@ abstract class _Metadata implements Metadata {
|
||||
@override
|
||||
String get host;
|
||||
@override
|
||||
String get process;
|
||||
@override
|
||||
String get remoteDestination;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
|
||||
@@ -15,6 +15,7 @@ _$MetadataImpl _$$MetadataImplFromJson(Map<String, dynamic> json) =>
|
||||
destinationIP: json['destinationIP'] as String,
|
||||
destinationPort: json['destinationPort'] as String,
|
||||
host: json['host'] as String,
|
||||
process: json['process'] as String,
|
||||
remoteDestination: json['remoteDestination'] as String,
|
||||
);
|
||||
|
||||
@@ -27,6 +28,7 @@ Map<String, dynamic> _$$MetadataImplToJson(_$MetadataImpl instance) =>
|
||||
'destinationIP': instance.destinationIP,
|
||||
'destinationPort': instance.destinationPort,
|
||||
'host': instance.host,
|
||||
'process': instance.process,
|
||||
'remoteDestination': instance.remoteDestination,
|
||||
};
|
||||
|
||||
|
||||
@@ -848,10 +848,8 @@ Process _$ProcessFromJson(Map<String, dynamic> json) {
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Process {
|
||||
int get uid => throw _privateConstructorUsedError;
|
||||
String get network => throw _privateConstructorUsedError;
|
||||
String get source => throw _privateConstructorUsedError;
|
||||
String get target => throw _privateConstructorUsedError;
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
Metadata get metadata => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
@@ -863,7 +861,9 @@ abstract class $ProcessCopyWith<$Res> {
|
||||
factory $ProcessCopyWith(Process value, $Res Function(Process) then) =
|
||||
_$ProcessCopyWithImpl<$Res, Process>;
|
||||
@useResult
|
||||
$Res call({int uid, String network, String source, String target});
|
||||
$Res call({int id, Metadata metadata});
|
||||
|
||||
$MetadataCopyWith<$Res> get metadata;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -879,30 +879,28 @@ class _$ProcessCopyWithImpl<$Res, $Val extends Process>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? uid = null,
|
||||
Object? network = null,
|
||||
Object? source = null,
|
||||
Object? target = null,
|
||||
Object? id = null,
|
||||
Object? metadata = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
uid: null == uid
|
||||
? _value.uid
|
||||
: uid // ignore: cast_nullable_to_non_nullable
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
network: null == network
|
||||
? _value.network
|
||||
: network // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
source: null == source
|
||||
? _value.source
|
||||
: source // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
target: null == target
|
||||
? _value.target
|
||||
: target // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
metadata: null == metadata
|
||||
? _value.metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Metadata,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$MetadataCopyWith<$Res> get metadata {
|
||||
return $MetadataCopyWith<$Res>(_value.metadata, (value) {
|
||||
return _then(_value.copyWith(metadata: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -912,7 +910,10 @@ abstract class _$$ProcessImplCopyWith<$Res> implements $ProcessCopyWith<$Res> {
|
||||
__$$ProcessImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({int uid, String network, String source, String target});
|
||||
$Res call({int id, Metadata metadata});
|
||||
|
||||
@override
|
||||
$MetadataCopyWith<$Res> get metadata;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -926,28 +927,18 @@ class __$$ProcessImplCopyWithImpl<$Res>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? uid = null,
|
||||
Object? network = null,
|
||||
Object? source = null,
|
||||
Object? target = null,
|
||||
Object? id = null,
|
||||
Object? metadata = null,
|
||||
}) {
|
||||
return _then(_$ProcessImpl(
|
||||
uid: null == uid
|
||||
? _value.uid
|
||||
: uid // ignore: cast_nullable_to_non_nullable
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
network: null == network
|
||||
? _value.network
|
||||
: network // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
source: null == source
|
||||
? _value.source
|
||||
: source // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
target: null == target
|
||||
? _value.target
|
||||
: target // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
metadata: null == metadata
|
||||
? _value.metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Metadata,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -955,27 +946,19 @@ class __$$ProcessImplCopyWithImpl<$Res>
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ProcessImpl implements _Process {
|
||||
const _$ProcessImpl(
|
||||
{required this.uid,
|
||||
required this.network,
|
||||
required this.source,
|
||||
required this.target});
|
||||
const _$ProcessImpl({required this.id, required this.metadata});
|
||||
|
||||
factory _$ProcessImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ProcessImplFromJson(json);
|
||||
|
||||
@override
|
||||
final int uid;
|
||||
final int id;
|
||||
@override
|
||||
final String network;
|
||||
@override
|
||||
final String source;
|
||||
@override
|
||||
final String target;
|
||||
final Metadata metadata;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Process(uid: $uid, network: $network, source: $source, target: $target)';
|
||||
return 'Process(id: $id, metadata: $metadata)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -983,15 +966,14 @@ class _$ProcessImpl implements _Process {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ProcessImpl &&
|
||||
(identical(other.uid, uid) || other.uid == uid) &&
|
||||
(identical(other.network, network) || other.network == network) &&
|
||||
(identical(other.source, source) || other.source == source) &&
|
||||
(identical(other.target, target) || other.target == target));
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.metadata, metadata) ||
|
||||
other.metadata == metadata));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, uid, network, source, target);
|
||||
int get hashCode => Object.hash(runtimeType, id, metadata);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@@ -1009,27 +991,175 @@ class _$ProcessImpl implements _Process {
|
||||
|
||||
abstract class _Process implements Process {
|
||||
const factory _Process(
|
||||
{required final int uid,
|
||||
required final String network,
|
||||
required final String source,
|
||||
required final String target}) = _$ProcessImpl;
|
||||
{required final int id,
|
||||
required final Metadata metadata}) = _$ProcessImpl;
|
||||
|
||||
factory _Process.fromJson(Map<String, dynamic> json) = _$ProcessImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get uid;
|
||||
int get id;
|
||||
@override
|
||||
String get network;
|
||||
@override
|
||||
String get source;
|
||||
@override
|
||||
String get target;
|
||||
Metadata get metadata;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$ProcessImplCopyWith<_$ProcessImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ProcessMapItem _$ProcessMapItemFromJson(Map<String, dynamic> json) {
|
||||
return _ProcessMapItem.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ProcessMapItem {
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
String? get value => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$ProcessMapItemCopyWith<ProcessMapItem> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ProcessMapItemCopyWith<$Res> {
|
||||
factory $ProcessMapItemCopyWith(
|
||||
ProcessMapItem value, $Res Function(ProcessMapItem) then) =
|
||||
_$ProcessMapItemCopyWithImpl<$Res, ProcessMapItem>;
|
||||
@useResult
|
||||
$Res call({int id, String? value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ProcessMapItemCopyWithImpl<$Res, $Val extends ProcessMapItem>
|
||||
implements $ProcessMapItemCopyWith<$Res> {
|
||||
_$ProcessMapItemCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? value = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
value: freezed == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ProcessMapItemImplCopyWith<$Res>
|
||||
implements $ProcessMapItemCopyWith<$Res> {
|
||||
factory _$$ProcessMapItemImplCopyWith(_$ProcessMapItemImpl value,
|
||||
$Res Function(_$ProcessMapItemImpl) then) =
|
||||
__$$ProcessMapItemImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({int id, String? value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ProcessMapItemImplCopyWithImpl<$Res>
|
||||
extends _$ProcessMapItemCopyWithImpl<$Res, _$ProcessMapItemImpl>
|
||||
implements _$$ProcessMapItemImplCopyWith<$Res> {
|
||||
__$$ProcessMapItemImplCopyWithImpl(
|
||||
_$ProcessMapItemImpl _value, $Res Function(_$ProcessMapItemImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? value = freezed,
|
||||
}) {
|
||||
return _then(_$ProcessMapItemImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
value: freezed == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ProcessMapItemImpl implements _ProcessMapItem {
|
||||
const _$ProcessMapItemImpl({required this.id, this.value});
|
||||
|
||||
factory _$ProcessMapItemImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ProcessMapItemImplFromJson(json);
|
||||
|
||||
@override
|
||||
final int id;
|
||||
@override
|
||||
final String? value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProcessMapItem(id: $id, value: $value)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ProcessMapItemImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.value, value) || other.value == value));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id, value);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProcessMapItemImplCopyWith<_$ProcessMapItemImpl> get copyWith =>
|
||||
__$$ProcessMapItemImplCopyWithImpl<_$ProcessMapItemImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$ProcessMapItemImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ProcessMapItem implements ProcessMapItem {
|
||||
const factory _ProcessMapItem({required final int id, final String? value}) =
|
||||
_$ProcessMapItemImpl;
|
||||
|
||||
factory _ProcessMapItem.fromJson(Map<String, dynamic> json) =
|
||||
_$ProcessMapItemImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get id;
|
||||
@override
|
||||
String? get value;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$ProcessMapItemImplCopyWith<_$ProcessMapItemImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ExternalProvider _$ExternalProviderFromJson(Map<String, dynamic> json) {
|
||||
return _ExternalProvider.fromJson(json);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ const _$MessageTypeEnumMap = {
|
||||
MessageType.delay: 'delay',
|
||||
MessageType.process: 'process',
|
||||
MessageType.now: 'now',
|
||||
MessageType.request: 'request',
|
||||
};
|
||||
|
||||
_$DelayImpl _$$DelayImplFromJson(Map<String, dynamic> json) => _$DelayImpl(
|
||||
@@ -81,18 +82,27 @@ Map<String, dynamic> _$$NowImplToJson(_$NowImpl instance) => <String, dynamic>{
|
||||
|
||||
_$ProcessImpl _$$ProcessImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ProcessImpl(
|
||||
uid: (json['uid'] as num).toInt(),
|
||||
network: json['network'] as String,
|
||||
source: json['source'] as String,
|
||||
target: json['target'] as String,
|
||||
id: (json['id'] as num).toInt(),
|
||||
metadata: Metadata.fromJson(json['metadata'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProcessImplToJson(_$ProcessImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'uid': instance.uid,
|
||||
'network': instance.network,
|
||||
'source': instance.source,
|
||||
'target': instance.target,
|
||||
'id': instance.id,
|
||||
'metadata': instance.metadata,
|
||||
};
|
||||
|
||||
_$ProcessMapItemImpl _$$ProcessMapItemImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ProcessMapItemImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
value: json['value'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProcessMapItemImplToJson(
|
||||
_$ProcessMapItemImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'value': instance.value,
|
||||
};
|
||||
|
||||
_$ExternalProviderImpl _$$ExternalProviderImplFromJson(
|
||||
|
||||
@@ -24,10 +24,10 @@ Profile _$ProfileFromJson(Map<String, dynamic> json) => Profile(
|
||||
id: json['id'] as String?,
|
||||
label: json['label'] as String?,
|
||||
url: json['url'] as String?,
|
||||
currentGroupName: json['currentGroupName'] as String?,
|
||||
userInfo: json['userInfo'] == null
|
||||
? null
|
||||
: UserInfo.fromJson(json['userInfo'] as Map<String, dynamic>),
|
||||
proxyName: json['proxyName'] as String?,
|
||||
lastUpdateDate: json['lastUpdateDate'] == null
|
||||
? null
|
||||
: DateTime.parse(json['lastUpdateDate'] as String),
|
||||
@@ -43,7 +43,7 @@ Profile _$ProfileFromJson(Map<String, dynamic> json) => Profile(
|
||||
Map<String, dynamic> _$ProfileToJson(Profile instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'label': instance.label,
|
||||
'proxyName': instance.proxyName,
|
||||
'currentGroupName': instance.currentGroupName,
|
||||
'url': instance.url,
|
||||
'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(),
|
||||
'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds,
|
||||
|
||||
@@ -1583,6 +1583,7 @@ abstract class _ProxiesCardSelectorState implements ProxiesCardSelectorState {
|
||||
/// @nodoc
|
||||
mixin _$ProxiesSelectorState {
|
||||
List<String> get groupNames => throw _privateConstructorUsedError;
|
||||
String? get currentGroupName => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$ProxiesSelectorStateCopyWith<ProxiesSelectorState> get copyWith =>
|
||||
@@ -1595,7 +1596,7 @@ abstract class $ProxiesSelectorStateCopyWith<$Res> {
|
||||
$Res Function(ProxiesSelectorState) then) =
|
||||
_$ProxiesSelectorStateCopyWithImpl<$Res, ProxiesSelectorState>;
|
||||
@useResult
|
||||
$Res call({List<String> groupNames});
|
||||
$Res call({List<String> groupNames, String? currentGroupName});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1613,12 +1614,17 @@ class _$ProxiesSelectorStateCopyWithImpl<$Res,
|
||||
@override
|
||||
$Res call({
|
||||
Object? groupNames = null,
|
||||
Object? currentGroupName = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
groupNames: null == groupNames
|
||||
? _value.groupNames
|
||||
: groupNames // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
currentGroupName: freezed == currentGroupName
|
||||
? _value.currentGroupName
|
||||
: currentGroupName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -1631,7 +1637,7 @@ abstract class _$$ProxiesSelectorStateImplCopyWith<$Res>
|
||||
__$$ProxiesSelectorStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({List<String> groupNames});
|
||||
$Res call({List<String> groupNames, String? currentGroupName});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1646,12 +1652,17 @@ class __$$ProxiesSelectorStateImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? groupNames = null,
|
||||
Object? currentGroupName = freezed,
|
||||
}) {
|
||||
return _then(_$ProxiesSelectorStateImpl(
|
||||
groupNames: null == groupNames
|
||||
? _value._groupNames
|
||||
: groupNames // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
currentGroupName: freezed == currentGroupName
|
||||
? _value.currentGroupName
|
||||
: currentGroupName // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1659,7 +1670,8 @@ class __$$ProxiesSelectorStateImplCopyWithImpl<$Res>
|
||||
/// @nodoc
|
||||
|
||||
class _$ProxiesSelectorStateImpl implements _ProxiesSelectorState {
|
||||
const _$ProxiesSelectorStateImpl({required final List<String> groupNames})
|
||||
const _$ProxiesSelectorStateImpl(
|
||||
{required final List<String> groupNames, required this.currentGroupName})
|
||||
: _groupNames = groupNames;
|
||||
|
||||
final List<String> _groupNames;
|
||||
@@ -1670,9 +1682,12 @@ class _$ProxiesSelectorStateImpl implements _ProxiesSelectorState {
|
||||
return EqualUnmodifiableListView(_groupNames);
|
||||
}
|
||||
|
||||
@override
|
||||
final String? currentGroupName;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProxiesSelectorState(groupNames: $groupNames)';
|
||||
return 'ProxiesSelectorState(groupNames: $groupNames, currentGroupName: $currentGroupName)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1681,12 +1696,14 @@ class _$ProxiesSelectorStateImpl implements _ProxiesSelectorState {
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ProxiesSelectorStateImpl &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._groupNames, _groupNames));
|
||||
.equals(other._groupNames, _groupNames) &&
|
||||
(identical(other.currentGroupName, currentGroupName) ||
|
||||
other.currentGroupName == currentGroupName));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, const DeepCollectionEquality().hash(_groupNames));
|
||||
int get hashCode => Object.hash(runtimeType,
|
||||
const DeepCollectionEquality().hash(_groupNames), currentGroupName);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@@ -1699,11 +1716,14 @@ class _$ProxiesSelectorStateImpl implements _ProxiesSelectorState {
|
||||
|
||||
abstract class _ProxiesSelectorState implements ProxiesSelectorState {
|
||||
const factory _ProxiesSelectorState(
|
||||
{required final List<String> groupNames}) = _$ProxiesSelectorStateImpl;
|
||||
{required final List<String> groupNames,
|
||||
required final String? currentGroupName}) = _$ProxiesSelectorStateImpl;
|
||||
|
||||
@override
|
||||
List<String> get groupNames;
|
||||
@override
|
||||
String? get currentGroupName;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$ProxiesSelectorStateImplCopyWith<_$ProxiesSelectorStateImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
|
||||
@@ -62,7 +62,7 @@ class UserInfo {
|
||||
class Profile {
|
||||
String id;
|
||||
String? label;
|
||||
String? proxyName;
|
||||
String? currentGroupName;
|
||||
String? url;
|
||||
DateTime? lastUpdateDate;
|
||||
Duration autoUpdateDuration;
|
||||
@@ -74,8 +74,8 @@ class Profile {
|
||||
String? id,
|
||||
this.label,
|
||||
this.url,
|
||||
this.currentGroupName,
|
||||
this.userInfo,
|
||||
this.proxyName,
|
||||
this.lastUpdateDate,
|
||||
SelectedMap? selectedMap,
|
||||
Duration? autoUpdateDuration,
|
||||
@@ -134,6 +134,7 @@ class Profile {
|
||||
return _$ProfileFromJson(json);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
@@ -141,34 +142,31 @@ class Profile {
|
||||
runtimeType == other.runtimeType &&
|
||||
id == other.id &&
|
||||
label == other.label &&
|
||||
proxyName == other.proxyName &&
|
||||
currentGroupName == other.currentGroupName &&
|
||||
url == other.url &&
|
||||
lastUpdateDate == other.lastUpdateDate &&
|
||||
autoUpdateDuration == other.autoUpdateDuration &&
|
||||
userInfo == other.userInfo &&
|
||||
autoUpdate == other.autoUpdate;
|
||||
autoUpdate == other.autoUpdate &&
|
||||
selectedMap == other.selectedMap;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
id.hashCode ^
|
||||
label.hashCode ^
|
||||
proxyName.hashCode ^
|
||||
currentGroupName.hashCode ^
|
||||
url.hashCode ^
|
||||
lastUpdateDate.hashCode ^
|
||||
autoUpdateDuration.hashCode ^
|
||||
userInfo.hashCode ^
|
||||
autoUpdate.hashCode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Profile{id: $id, label: $label, proxyName: $proxyName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, userInfo: $userInfo, autoUpdate: $autoUpdate}';
|
||||
}
|
||||
autoUpdate.hashCode ^
|
||||
selectedMap.hashCode;
|
||||
|
||||
Profile copyWith({
|
||||
String? label,
|
||||
String? url,
|
||||
UserInfo? userInfo,
|
||||
String? groupName,
|
||||
String? currentGroupName,
|
||||
String? proxyName,
|
||||
DateTime? lastUpdateDate,
|
||||
Duration? autoUpdateDuration,
|
||||
@@ -179,7 +177,7 @@ class Profile {
|
||||
id: id,
|
||||
label: label ?? this.label,
|
||||
url: url ?? this.url,
|
||||
proxyName: proxyName ?? this.proxyName,
|
||||
currentGroupName: currentGroupName ?? this.currentGroupName,
|
||||
userInfo: userInfo ?? this.userInfo,
|
||||
selectedMap: selectedMap ?? this.selectedMap,
|
||||
lastUpdateDate: lastUpdateDate ?? this.lastUpdateDate,
|
||||
|
||||
@@ -94,6 +94,7 @@ class ProxiesCardSelectorState with _$ProxiesCardSelectorState {
|
||||
class ProxiesSelectorState with _$ProxiesSelectorState {
|
||||
const factory ProxiesSelectorState({
|
||||
required List<String> groupNames,
|
||||
required String? currentGroupName,
|
||||
}) = _ProxiesSelectorState;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,11 @@ class App {
|
||||
methodChannel = const MethodChannel("app");
|
||||
methodChannel!.setMethodCallHandler((call) async {
|
||||
switch (call.method) {
|
||||
case "exit":
|
||||
if (onExit != null) {
|
||||
await onExit!();
|
||||
}
|
||||
break;
|
||||
case "gc":
|
||||
clashCore.requestGc();
|
||||
break;
|
||||
@@ -63,9 +68,9 @@ class App {
|
||||
});
|
||||
}
|
||||
|
||||
Future<String?> getPackageName(Metadata metadata) async {
|
||||
return await methodChannel?.invokeMethod<String>("getPackageName", {
|
||||
"data": json.encode(metadata),
|
||||
Future<String?> resolverProcess(Process process) async {
|
||||
return await methodChannel?.invokeMethod<String>("resolverProcess", {
|
||||
"data": json.encode(process),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,6 @@ class Proxy extends ProxyPlatform {
|
||||
return await methodChannel.invokeMethod<bool?>("SetProtect", {'fd': fd});
|
||||
}
|
||||
|
||||
Future<int?> getRunTimeStamp() async {
|
||||
return await methodChannel.invokeMethod<int?>("GetRunTimeStamp");
|
||||
}
|
||||
|
||||
Future<bool?> startForeground({
|
||||
required String title,
|
||||
required String content,
|
||||
@@ -80,14 +76,7 @@ class Proxy extends ProxyPlatform {
|
||||
}
|
||||
|
||||
updateStartTime() async {
|
||||
startTime = await getRunTime();
|
||||
}
|
||||
|
||||
Future<DateTime?> getRunTime() async {
|
||||
final runTimeStamp = await getRunTimeStamp();
|
||||
return runTimeStamp != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(runTimeStamp)
|
||||
: null;
|
||||
startTime = clashCore.getRunTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ class GlobalState {
|
||||
Function? updateCurrentDelayDebounce;
|
||||
PageController? pageController;
|
||||
final navigatorKey = GlobalKey<NavigatorState>();
|
||||
final Map<int, String?> packageNameMap = {};
|
||||
late AppController appController;
|
||||
GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
|
||||
List<Function> updateFunctionLists = [];
|
||||
@@ -212,30 +211,30 @@ class GlobalState {
|
||||
required String message,
|
||||
SnackBarAction? action,
|
||||
}) {
|
||||
final width = context.width;
|
||||
EdgeInsets margin;
|
||||
if (width < 600) {
|
||||
margin = const EdgeInsets.only(
|
||||
bottom: 96,
|
||||
right: 16,
|
||||
left: 16,
|
||||
);
|
||||
} else {
|
||||
margin = EdgeInsets.only(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: width - 316,
|
||||
);
|
||||
}
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
action: action,
|
||||
content: Text(message),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
duration: const Duration(milliseconds: 1500),
|
||||
margin: margin,
|
||||
),
|
||||
);
|
||||
// final width = context.width;
|
||||
// EdgeInsets margin;
|
||||
// if (width < 600) {
|
||||
// margin = const EdgeInsets.only(
|
||||
// bottom: 96,
|
||||
// right: 16,
|
||||
// left: 16,
|
||||
// );
|
||||
// } else {
|
||||
// margin = EdgeInsets.only(
|
||||
// bottom: 16,
|
||||
// left: 16,
|
||||
// right: width - 316,
|
||||
// );
|
||||
// }
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(
|
||||
// action: action,
|
||||
// content: Text(message),
|
||||
// behavior: SnackBarBehavior.floating,
|
||||
// duration: const Duration(milliseconds: 1500),
|
||||
// margin: margin,
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
|
||||
Future<T?> safeRun<T>(
|
||||
|
||||
@@ -56,9 +56,20 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
|
||||
}
|
||||
|
||||
@override
|
||||
void onProcess(Metadata metadata) async {
|
||||
var packageName = await app?.getPackageName(metadata);
|
||||
globalState.packageNameMap[metadata.uid] = packageName;
|
||||
super.onProcess(metadata);
|
||||
void onProcess(Process process) async {
|
||||
var packageName = await app?.resolverProcess(process);
|
||||
clashCore.setProcessMap(
|
||||
ProcessMapItem(
|
||||
id: process.id,
|
||||
value: packageName,
|
||||
),
|
||||
);
|
||||
super.onProcess(process);
|
||||
}
|
||||
|
||||
@override
|
||||
void onRequest(Connection connection) async {
|
||||
globalState.appController.appState.addRequest(connection);
|
||||
super.onRequest(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,7 @@ class CommonScaffoldState extends State<CommonScaffold> {
|
||||
return floatingActionButton ?? Container();
|
||||
},
|
||||
),
|
||||
resizeToAvoidBottomInset: true,
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(kToolbarHeight),
|
||||
child: Stack(
|
||||
|
||||
Reference in New Issue
Block a user