feat(client/macos): 开机自启用 SMAppService + LoginItem helper(学 Tailscale)
根因:launch_at_startup 0.5.1 在 macOS 无自带实现,需宿主接 MethodChannel,而我们 Runner 从没实现 → enable() 抛 MissingPluginException 被 try/catch 吞掉 → 登录项从未注册,自启失败。 学 Tailscale(LoginItemHelper-macsys + SMAppService): - 新增无界面 LoginItem helper(macos/login_helper,LSBackgroundOnly),登录时带 --autostart 拉起主 app(→隐藏到托盘); - build_login_helper.sh 构建期编译+签名+嵌入 Contents/Library/LoginItems/(新 Run Script 阶段); - MainFlutterWindow 接 launch_at_startup MethodChannel → SMAppService.loginItem 注册/注销。 构建通过:helper 嵌入+签名,主 app --deep --strict 校验过。Windows 自启(schtasks)不变。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import ServiceManagement
|
||||
|
||||
class MainFlutterWindow: NSWindow {
|
||||
// launch_at_startup 插件在 macOS 没自带实现,需宿主接 MethodChannel。学 Tailscale 用
|
||||
// SMAppService.loginItem 注册内置的 PangolinLoginHelper(登录时它带 --autostart 拉起主 app)。
|
||||
private let loginHelperID = "com.pangolin.pangolin.LoginHelper"
|
||||
|
||||
override func awakeFromNib() {
|
||||
let flutterViewController = FlutterViewController()
|
||||
self.contentViewController = flutterViewController
|
||||
@@ -18,6 +23,9 @@ class MainFlutterWindow: NSWindow {
|
||||
// 需 VpnChannel.swift 已加入 Runner target 的 Compile Sources。
|
||||
VpnChannel.register(with: flutterViewController.registrar(forPlugin: "VpnChannel"))
|
||||
|
||||
// 开机自启 MethodChannel(launch_at_startup 插件的原生侧,macOS 需宿主实现)。
|
||||
registerLaunchAtStartupChannel(messenger: flutterViewController.engine.binaryMessenger)
|
||||
|
||||
super.awakeFromNib()
|
||||
|
||||
// 开机自启(登录项带 --autostart):不弹主窗口,直接隐藏到托盘(与 Windows 一致)。
|
||||
@@ -27,4 +35,36 @@ class MainFlutterWindow: NSWindow {
|
||||
self.orderOut(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// launch_at_startup 的 macOS 原生实现:SMAppService.loginItem 注册/注销 LoginHelper。
|
||||
private func registerLaunchAtStartupChannel(messenger: FlutterBinaryMessenger) {
|
||||
let channel = FlutterMethodChannel(name: "launch_at_startup", binaryMessenger: messenger)
|
||||
let helperID = loginHelperID
|
||||
channel.setMethodCallHandler { call, result in
|
||||
guard #available(macOS 13.0, *) else {
|
||||
result(FlutterError(code: "unsupported", message: "开机自启需 macOS 13+", details: nil))
|
||||
return
|
||||
}
|
||||
let svc = SMAppService.loginItem(identifier: helperID)
|
||||
switch call.method {
|
||||
case "launchAtStartupIsEnabled":
|
||||
result(svc.status == .enabled)
|
||||
case "launchAtStartupSetEnabled":
|
||||
let on = ((call.arguments as? [String: Any])?["setEnabledValue"] as? Bool) ?? false
|
||||
do {
|
||||
if on {
|
||||
if svc.status != .enabled { try svc.register() }
|
||||
} else {
|
||||
if svc.status == .enabled { try svc.unregister() }
|
||||
}
|
||||
result(nil)
|
||||
} catch {
|
||||
NSLog("[pangolin/launch] SMAppService %@ failed: %@", on ? "register" : "unregister", error.localizedDescription)
|
||||
result(FlutterError(code: "smappservice", message: error.localizedDescription, details: nil))
|
||||
}
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user