mac程序坞退出清理事件

This commit is contained in:
wanghongenpin
2023-12-21 02:45:37 +08:00
parent 36d2b8f381
commit 1d083b4d05
16 changed files with 139 additions and 52 deletions

View File

@@ -21,6 +21,10 @@ import com.network.proxy.vpn.socket.ProtectSocketHolder
class ProxyVpnService : VpnService(), ProtectSocket {
private var vpnInterface: ParcelFileDescriptor? = null
private var host: String? = null
private var port: Int = 0
private var allowApps: List<String>? = null
companion object {
const val MAX_PACKET_LEN = 1500

View File

@@ -0,0 +1,11 @@
package com.network.proxy.vpn
fun formatTag(tag: String): String {
return tag
}
val Any.TAG: String
get() {
return javaClass.name
}

View File

@@ -1,5 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
abstract interface class AppLifecycleListener {
void onUserLeaveHint(AppLifecycleState state);
void onDetached(AppLifecycleState state);
}
class AppLifecycleBinding {
static const MethodChannel _methodChannel = MethodChannel('com.proxy/appLifecycle');
static bool _initialized = false;
static ensureInitialized() {
if (_initialized) {
return;
}
//注册方法
_methodChannel.setMethodCallHandler(_methodCallHandler);
_initialized = true;
}
static Future<void> _methodCallHandler(MethodCall call) async {
switch (call.method) {
case 'appDetached':
await WidgetsBinding.instance.handleRequestAppExit();
break;
case 'userLeaveHint':
print("userLeaveHint");
// WidgetsBinding.instance.handleRequestAppExit();
break;
}
return Future.value();
}
}

View File

@@ -87,10 +87,14 @@ class ProxyServer {
/// 停止代理服务
Future<Server?> stop() async {
logger.i("stop on $port");
if (!isRunning) {
return server;
}
if (configuration.enableSystemProxy) {
await setSystemProxyEnable(false);
}
logger.i("stop on $port");
await server?.stop();
return server;
}

View File

@@ -147,8 +147,9 @@ class _DesktopHomePagePageState extends State<DesktopHomePage> implements EventL
'1. 请求重写增加 修改请求,可根据正则替换;\n'
'2. 请求重写批量导入、导出;\n'
'3. 支持WebSocket抓包\n'
'4. 优化curl导入\n'
'5. 支持head请求修复手机端请求重写切换应用恢复原始的请求问题\n'
'4. 安卓支持小窗口模式\n'
'5. 优化curl导入\n'
'6. 支持head请求修复手机端请求重写切换应用恢复原始的请求问题\n'
'',
style: TextStyle(fontSize: 14)));
});

View File

@@ -46,7 +46,6 @@ class _ToolbarState extends State<Toolbar> {
}
if (event.isKeyPressed(LogicalKeyboardKey.metaLeft) && event.isKeyPressed(LogicalKeyboardKey.keyQ)) {
print("windowManager.close()");
windowManager.close();
return;
}

View File

@@ -1,7 +1,9 @@
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_toastr/flutter_toastr.dart';
import 'package:network_proxy/native/app_lifecycle.dart';
import 'package:network_proxy/network/bin/server.dart';
import 'package:network_proxy/utils/platform.dart';
import 'package:window_manager/window_manager.dart';
@@ -38,8 +40,8 @@ class _SocketLaunchState extends State<SocketLaunch> with WindowListener, Widget
super.initState();
windowManager.addListener(this);
WidgetsBinding.instance.addObserver(this);
AppLifecycleBinding.ensureInitialized();
//启动代理服务器
print("SocketLaunch ${widget.startup}");
if (widget.startup) {
start();
}
@@ -57,13 +59,23 @@ class _SocketLaunchState extends State<SocketLaunch> with WindowListener, Widget
@override
void onWindowClose() async {
await widget.proxyServer.stop();
print("onWindowClose");
await appExit();
}
Future<void> appExit() async {
await widget.proxyServer.stop();
SocketLaunch.started = false;
await windowManager.destroy();
exit(0);
}
@override
Future<AppExitResponse> didRequestAppExit() async {
await appExit();
return super.didRequestAppExit();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.detached) {
@@ -76,8 +88,6 @@ class _SocketLaunchState extends State<SocketLaunch> with WindowListener, Widget
@override
Widget build(BuildContext context) {
print("SocketLaunch build ${widget.startup}");
return IconButton(
tooltip: SocketLaunch.started ? "停止" : "启动",
icon: Icon(SocketLaunch.started ? Icons.stop : Icons.play_arrow_sharp,

View File

@@ -40,8 +40,6 @@ class MobileHomeState extends State<MobileHomePage> with WidgetsBindingObserver
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
print("didChangeAppLifecycleState $state");
if (state == AppLifecycleState.inactive && Vpn.isVpnStarted) {
if (desktop.value.connect || !Platform.isAndroid || !widget.configuration.smallWindow) {
return;
@@ -53,7 +51,6 @@ class MobileHomeState extends State<MobileHomePage> with WidgetsBindingObserver
if (state == AppLifecycleState.resumed && pictureInPictureNotifier.value) {
Vpn.isRunning().then((value) {
Vpn.isVpnStarted = value;
print("isRunning $value");
pictureInPictureNotifier.value = false;
});
}
@@ -165,8 +162,9 @@ class MobileHomeState extends State<MobileHomePage> with WidgetsBindingObserver
'1. 请求重写增加 修改请求,可根据增则替换;\n'
'2. 请求重写批量导入、导出;\n'
'3. 支持WebSocket抓包\n'
'4. 优化curl导入\n'
'5. 支持head请求修复手机端请求重写切换应用恢复原始的请求问题\n'
'4. 安卓支持小窗口模式\n'
'5. 优化curl导入\n'
'6. 支持head请求修复手机端请求重写切换应用恢复原始的请求问题\n'
'';
showAlertDialog('更新内容V1.0.6', content, () {
widget.configuration.upgradeNoticeV6 = false;

View File

@@ -69,8 +69,7 @@ class _FavoriteItem extends StatefulWidget {
final ProxyServer proxyServer;
final Function(Favorite favorite)? onRemove;
const _FavoriteItem(this.favorite, {Key? key, required this.onRemove, required this.proxyServer, required this.index})
: super(key: key);
const _FavoriteItem(this.favorite, {super.key, required this.onRemove, required this.proxyServer, required this.index});
@override
State<_FavoriteItem> createState() => _FavoriteItemState();
@@ -156,7 +155,7 @@ class _FavoriteItemState extends State<_FavoriteItem> {
Container(color: Theme.of(context).hoverColor, height: 8),
TextButton(
child: Container(
height: 40,
height: 55,
width: double.infinity,
padding: const EdgeInsets.only(top: 10),
child: const Text("取消", textAlign: TextAlign.center)),

View File

@@ -461,7 +461,7 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
),
TextButton(
child: Container(
height: 60,
height: 55,
width: double.infinity,
padding: const EdgeInsets.only(top: 10),
child: const Text("取消", textAlign: TextAlign.center)),

View File

@@ -145,7 +145,7 @@ class RequestRowState extends State<RequestRow> {
),
TextButton(
child: Container(
height: 40,
height: 55,
width: double.infinity,
padding: const EdgeInsets.only(top: 10),
child: const Text("取消", textAlign: TextAlign.center)),

View File

@@ -337,7 +337,7 @@ class _RequestRuleListState extends State<RequestRuleList> {
Container(color: Theme.of(context).hoverColor, height: 8),
TextButton(
child: Container(
height: 48,
height: 55,
width: double.infinity,
padding: const EdgeInsets.only(top: 10),
child: const Text("取消", textAlign: TextAlign.center)),

View File

@@ -30,6 +30,7 @@
59CD0B3B69B2AD63E8F7FD5B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15B6D3EA3FFF7C54E0E30275 /* Pods_Runner.framework */; };
85F740723892A1960748773D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C326C0FC3A1C8C78354E1D3 /* Pods_RunnerTests.framework */; };
9B673CF12A383721009CB5B5 /* T.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B673CF02A383721009CB5B5 /* T.m */; };
9B8D91812B335386009B90B1 /* AppLifecycleChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8D91802B335386009B90B1 /* AppLifecycleChannel.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -87,6 +88,7 @@
8F664CC27DDA6C1AEED215C8 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
9B673CF02A383721009CB5B5 /* T.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = T.m; sourceTree = "<group>"; };
9B8D91802B335386009B90B1 /* AppLifecycleChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLifecycleChannel.swift; sourceTree = "<group>"; };
9BCACE942A3AAED1009FBC53 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = "<group>"; };
AEE2D821CE41DE974B8482C9 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
C037DD715A776D1345BC6EBC /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
@@ -191,6 +193,7 @@
33FAB671232836740065AC1E /* Runner */ = {
isa = PBXGroup;
children = (
9B8D91802B335386009B90B1 /* AppLifecycleChannel.swift */,
9BCACE942A3AAED1009FBC53 /* RunnerProfile.entitlements */,
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
@@ -443,6 +446,7 @@
files = (
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
9B8D91812B335386009B90B1 /* AppLifecycleChannel.swift in Sources */,
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -3,26 +3,28 @@ import FlutterMacOS
@NSApplicationMain
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
override func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if !flag {
for window in NSApp.windows {
if !window.isVisible {
window.setIsVisible(true)
}
window.makeKeyAndOrderFront(self)
NSApp.activate(ignoringOtherApps: true)
}
}
return true
}
override func applicationWillTerminate(_ notification: Notification) {
print("applicationWillTerminate called")
NSLog("applicationWillTerminate")
}
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
override func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if !flag {
for window in NSApp.windows {
if !window.isVisible {
window.setIsVisible(true)
}
window.makeKeyAndOrderFront(self)
NSApp.activate(ignoringOtherApps: true)
}
}
return true
}
override func applicationWillTerminate(_ notification: Notification) {
AppLifecycleChannel.appDetached()
NSLog("applicationWillTerminate")
}
}

View File

@@ -0,0 +1,22 @@
//
// AppLifecycleChannel.swift
//
// Created by wanghongen on 2023/12/21.
//
import Foundation
import FlutterMacOS
class AppLifecycleChannel {
static private var channel : FlutterMethodChannel?
//
static func registerChannel(flutterViewController: FlutterViewController) {
channel = FlutterMethodChannel(name: "com.proxy/appLifecycle", binaryMessenger: flutterViewController.engine.binaryMessenger)
}
static func appDetached() {
channel!.invokeMethod("appDetached", arguments: nil)
Thread.sleep(forTimeInterval: 0.5)
}
}

View File

@@ -2,15 +2,17 @@ import Cocoa
import FlutterMacOS
class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
let flutterViewController = FlutterViewController()
let windowFrame = self.frame
self.contentViewController = flutterViewController
self.setFrame(windowFrame, display: true)
RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib()
}
override func awakeFromNib() {
let flutterViewController = FlutterViewController()
let windowFrame = self.frame
self.contentViewController = flutterViewController
self.setFrame(windowFrame, display: true)
AppLifecycleChannel.registerChannel(flutterViewController: flutterViewController)
RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib()
}
}