From 75d6a65a2943c0d75f6302c72a43f6b9ea876d8c Mon Sep 17 00:00:00 2001 From: rgehbt Date: Sat, 31 Jan 2026 22:16:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BC=80=E6=9C=BA?= =?UTF-8?q?=E8=87=AA=E5=90=AF=E6=8E=A7=E5=88=B6=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MacEasySymbol/AppDelegate.swift | 14 ++++ MacEasySymbol/LoginItemManager.swift | 116 +++++++++++++++++++++++++++ MacEasySymbol/StatusBarManager.swift | 26 ++++++ 3 files changed, 156 insertions(+) create mode 100644 MacEasySymbol/LoginItemManager.swift diff --git a/MacEasySymbol/AppDelegate.swift b/MacEasySymbol/AppDelegate.swift index 107d6f4..45a240e 100644 --- a/MacEasySymbol/AppDelegate.swift +++ b/MacEasySymbol/AppDelegate.swift @@ -240,6 +240,20 @@ extension AppDelegate: StatusBarManagerDelegate { showWhitelistSettingsWindow() } + func statusBarManagerDidRequestToggleLoginItem(_ manager: StatusBarManager) { + let currentStatus = LoginItemManager.shared.getCurrentStatus() + let newStatus = !currentStatus + + let success = LoginItemManager.shared.setLoginItemEnabled(newStatus) + + if success { + statusBarManager?.updateLoginItemStatus(newStatus) + DebugLogger.log("🔄 开机自启已\(newStatus ? "启用" : "禁用")") + } else { + DebugLogger.logError("❌ 开机自启设置失败") + } + } + func statusBarManagerDidRequestQuit(_ manager: StatusBarManager) { // 确保在退出前完整清理资源 cleanupResources() diff --git a/MacEasySymbol/LoginItemManager.swift b/MacEasySymbol/LoginItemManager.swift new file mode 100644 index 0000000..2670d89 --- /dev/null +++ b/MacEasySymbol/LoginItemManager.swift @@ -0,0 +1,116 @@ +import Cocoa +import ServiceManagement + +class LoginItemManager { + + static let shared = LoginItemManager() + + private let launchAgentFileName = "com.rivermao.maceasysymbol.MacEasySymbol.plist" + private let userDefaultsKey = "LoginItemEnabled" + + private init() {} + + var isEnabled: Bool { + get { + return UserDefaults.standard.bool(forKey: userDefaultsKey) + } + set { + UserDefaults.standard.set(newValue, forKey: userDefaultsKey) + } + } + + func setLoginItemEnabled(_ enabled: Bool) -> Bool { + if #available(macOS 13.0, *) { + return setLoginItemEnabledModern(enabled) + } else { + return setLoginItemEnabledLegacy(enabled) + } + } + + func getCurrentStatus() -> Bool { + if #available(macOS 13.0, *) { + return SMAppService.mainApp.status == .enabled + } else { + return FileManager.default.fileExists(atPath: getLaunchAgentPath()) + } + } + + @available(macOS 13.0, *) + private func setLoginItemEnabledModern(_ enabled: Bool) -> Bool { + do { + let status = SMAppService.mainApp.status + isEnabled = enabled + if enabled { + if status == .enabled { + DebugLogger.log("✅ 登录项已启用") + } else { + try SMAppService.mainApp.register() + DebugLogger.log("✅ 登录项已注册并启用") + } + } else { + if status == .notFound { + DebugLogger.log("✅ 登录项已禁用") + } else { + try SMAppService.mainApp.unregister() + DebugLogger.log("✅ 登录项已注销") + } + } + return true + } catch { + DebugLogger.logError("❌ 登录项操作失败: \(error.localizedDescription)") + return false + } + } + + private func setLoginItemEnabledLegacy(_ enabled: Bool) -> Bool { + let launchAgentPath = getLaunchAgentPath() + let fileManager = FileManager.default + + do { + if enabled { + let appPath = Bundle.main.bundlePath + guard !appPath.isEmpty else { + DebugLogger.logError("❌ 无法获取应用路径") + return false + } + + let plistContent: [String: Any] = [ + "Label": launchAgentFileName.replacingOccurrences(of: ".plist", with: ""), + "ProgramArguments": [appPath], + "RunAtLoad": true + ] + + let plistData = try PropertyListSerialization.data(fromPropertyList: plistContent, format: .xml, options: 0) + try plistData.write(to: URL(fileURLWithPath: launchAgentPath), options: .atomic) + + isEnabled = true + DebugLogger.log("✅ LaunchAgent 已创建并启用") + return true + } else { + if fileManager.fileExists(atPath: launchAgentPath) { + try fileManager.removeItem(atPath: launchAgentPath) + DebugLogger.log("✅ LaunchAgent 已删除") + } + + isEnabled = false + DebugLogger.log("✅ 登录项已禁用") + return true + } + } catch { + DebugLogger.logError("❌ LaunchAgent 操作失败: \(error.localizedDescription)") + return false + } + } + + private func getLaunchAgentPath() -> String { + let homeDir = FileManager.default.homeDirectoryForCurrentUser.path + let launchAgentsDir = (homeDir as NSString).appendingPathComponent("Library/LaunchAgents") + + let fileManager = FileManager.default + if !fileManager.fileExists(atPath: launchAgentsDir) { + try? fileManager.createDirectory(atPath: launchAgentsDir, withIntermediateDirectories: true, attributes: nil) + } + + return (launchAgentsDir as NSString).appendingPathComponent(launchAgentFileName) + } +} diff --git a/MacEasySymbol/StatusBarManager.swift b/MacEasySymbol/StatusBarManager.swift index 142df07..0dfe49c 100644 --- a/MacEasySymbol/StatusBarManager.swift +++ b/MacEasySymbol/StatusBarManager.swift @@ -13,6 +13,7 @@ protocol StatusBarManagerDelegate: AnyObject { func statusBarManagerDidRequestHotkeySettings(_ manager: StatusBarManager) func statusBarManagerDidRequestWhitelistSettings(_ manager: StatusBarManager) func statusBarManagerDidRequestSymbolSettings(_ manager: StatusBarManager) + func statusBarManagerDidRequestToggleLoginItem(_ manager: StatusBarManager) } class StatusBarManager: NSObject { @@ -27,9 +28,17 @@ class StatusBarManager: NSObject { } } + private var loginItemEnabled: Bool { + didSet { + updateMenu() + } + } + override init() { // 每次启动都强制设置为启用状态 self.isInterventionEnabled = true + // 读取登录项状态 + self.loginItemEnabled = LoginItemManager.shared.getCurrentStatus() super.init() // 确保UserDefaults也设置为启用状态 UserDefaults.standard.set(true, forKey: "InterventionEnabled") @@ -93,6 +102,11 @@ class StatusBarManager: NSObject { DebugLogger.log("🔄 通过全局快捷键切换介入模式: \(isInterventionEnabled ? "启用" : "禁用")") } + // 更新登录项状态 + func updateLoginItemStatus(_ enabled: Bool) { + loginItemEnabled = enabled + } + // MARK: - Private Methods private func updateStatusBarIcon() { @@ -162,6 +176,14 @@ class StatusBarManager: NSObject { menu.addItem(NSMenuItem.separator()) + // 开机自启 + let loginItemTitle = loginItemEnabled ? "✓ 开机自启" : "开机自启" + let loginItemMenuItem = NSMenuItem(title: loginItemTitle, action: #selector(toggleLoginItem), keyEquivalent: "") + loginItemMenuItem.target = self + menu.addItem(loginItemMenuItem) + + menu.addItem(NSMenuItem.separator()) + // 关于 let aboutItem = NSMenuItem(title: "关于", action: #selector(showAbout), keyEquivalent: "") aboutItem.target = self @@ -242,6 +264,10 @@ class StatusBarManager: NSObject { delegate?.statusBarManagerDidRequestWhitelistSettings(self) } + @objc private func toggleLoginItem() { + delegate?.statusBarManagerDidRequestToggleLoginItem(self) + } + @objc private func quitApp() { delegate?.statusBarManagerDidRequestQuit(self) }