From 041a4bb911bbb38a1d7985fc615ebc95f8ec297f Mon Sep 17 00:00:00 2001 From: leo12025 Date: Sat, 27 Dec 2025 18:39:01 +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=E5=8A=A8=E5=8A=9F=E8=83=BD=E5=92=8C=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=8C=87=E5=BC=95=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增开机自启动功能,使用SMAppService实现 - 添加用户首次使用指引界面,包含功能介绍和权限说明 - 更新菜单栏选项,增加自启动开关和查看指南按钮 - 添加应用权限配置文件PowerBar.entitlements - 创建README文档和.gitignore文件 --- .gitignore | 98 +++++++++++++++ PowerBar/AppState.swift | 38 ++++++ PowerBar/PowerBar.entitlements | 24 ++++ PowerBar/PowerBarApp.swift | 216 ++++++++++++++++++++++++++++++++- README.md | 58 +++++++++ 5 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 PowerBar/PowerBar.entitlements create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2e27e60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,98 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, too + +## Build generated +build/ +DerivedData/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Various settings +*.pbxproj +*.xccheckout +*.moved-aside +*.xcuserstate +*.xcscmblueprint +*.xccheckout + +## Other +*.moved-aside +*.xcuserstate +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## CocoaPods +Pods/ +Podfile.lock + +## Carthage +Carthage/Build/ + +## fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output + +## Bundle artifact +*.jsbundle + +## External dependencies +*.framework +*.a +*.dylib + +## App Store submission +*.ipa +*.app.dSYM.zip +*.app.dSYM + +## Local testing +*.xcodeproj/xcshareddata/xcschemes/ +*.xcodeproj/project.xcworkspace/xcuserdata/ + +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Icon must be in root +!Icon? + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# PowerBar specific +PowerBar.xcarchive/ +ExportedApp/ +PowerBar.dmg +ExportOptions.plist diff --git a/PowerBar/AppState.swift b/PowerBar/AppState.swift index 0968962..0011a3b 100644 --- a/PowerBar/AppState.swift +++ b/PowerBar/AppState.swift @@ -1,6 +1,7 @@ import Foundation import Combine import IOKit.pwr_mgt +import ServiceManagement // MARK: - App State Manager class AppState: ObservableObject { @@ -12,6 +13,9 @@ class AppState: ObservableObject { // 用户当前是否空闲 @Published var isUserIdle: Bool = false + // 自动启动设置 + @Published var isLaunchAtLogin: Bool = false + // 内部属性 private var monitorTimer: Timer? private var caffeinateProcess: Process? // 直接使用Process对象,但简化实现 @@ -19,6 +23,12 @@ class AppState: ObservableObject { // 配置参数 private let idleThreshold: TimeInterval = 300 // 5分钟无操作视为空闲 private let checkInterval: TimeInterval = 10 // 每10秒检查一次 + + // 初始化方法 + init() { + // 检查并设置初始的自动启动状态 + checkLaunchAtLoginStatus() + } // MARK: - Public Methods @@ -153,6 +163,34 @@ class AppState: ObservableObject { } } + // MARK: - Launch at Login Management + + /// 检查当前的自动启动状态 + private func checkLaunchAtLoginStatus() { + // 使用 Bundle.main.bundleIdentifier 获取应用的 bundle ID + guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return } + isLaunchAtLogin = SMAppService.mainApp.status == .enabled + } + + /// 切换自动启动状态 + func toggleLaunchAtLogin() { + isLaunchAtLogin.toggle() + + do { + if isLaunchAtLogin { + try SMAppService.mainApp.register() + print("已设置开机自动启动") + } else { + try SMAppService.mainApp.unregister() + print("已取消开机自动启动") + } + } catch { + print("设置开机自动启动失败: \(error)") + // 恢复原来的状态 + isLaunchAtLogin.toggle() + } + } + // MARK: - Deinitializer deinit { diff --git a/PowerBar/PowerBar.entitlements b/PowerBar/PowerBar.entitlements new file mode 100644 index 0000000..7a8b832 --- /dev/null +++ b/PowerBar/PowerBar.entitlements @@ -0,0 +1,24 @@ + + + + + + com.apple.security.automation.apple-events + + + + com.apple.security.iokit-user-client-class + + IOPMPowerSource + IOHIDSystem + + + + com.apple.security.shell + + + + com.apple.security.files.user-selected.read-only + + + \ No newline at end of file diff --git a/PowerBar/PowerBarApp.swift b/PowerBar/PowerBarApp.swift index 7393c40..5399f0d 100644 --- a/PowerBar/PowerBarApp.swift +++ b/PowerBar/PowerBarApp.swift @@ -4,6 +4,8 @@ import SwiftUI struct PowerBarApp: App { // 创建一个状态对象来管理 App 的核心逻辑 @StateObject private var appState = AppState() + // 跟踪是否显示指引界面 + @AppStorage("hasShownGuide") private var hasShownGuide = false var body: some Scene { // 使用 MenuBarExtra 创建一个状态栏应用 @@ -23,7 +25,18 @@ struct PowerBarApp: App { .foregroundColor(appState.isFunctionEnabled ? .green : .gray) } } - // 移除 setting,因为我们不需要设置窗口 + // 添加主窗口用于显示指引界面 + WindowGroup { + if !hasShownGuide { + GuideView() + .onDisappear { + hasShownGuide = true + } + } + } + .windowStyle(.hiddenTitleBar) + .defaultSize(width: 400, height: 400) + .windowResizability(.contentSize) } } @@ -117,6 +130,39 @@ struct MenuContent: View { Divider() + // 自动启动选项 + Toggle(isOn: Binding( + get: { appState.isLaunchAtLogin }, + set: { _ in appState.toggleLaunchAtLogin() } + )) { + HStack { + Image(systemName: "power.on.circle") + .frame(width: 20) + Text("开机自动启动") + } + } + .toggleStyle(.checkbox) + .font(.caption) + .padding(4) + + Divider() + + // 显示指引界面选项 + Button("查看使用指南") { + // 这里需要实现显示指引界面的逻辑 + // 我们可以使用 AppStorage 来控制指引界面的显示 + UserDefaults.standard.set(false, forKey: "hasShownGuide") + // 重启应用或打开新窗口显示指引界面 + // 由于 macOS 应用的限制,我们可以提示用户重新启动应用查看指引 + NSAlert.showAlert(title: "使用指南", message: "请重启应用以查看使用指南,或在下次启动时自动显示。") + } + .buttonStyle(.plain) + .font(.caption) + .foregroundColor(.secondary) + .padding(4) + + Divider() + // 退出应用 Button("退出 PowerBar") { NSApp.terminate(nil) @@ -135,3 +181,171 @@ struct MenuContent: View { ) } } + +// 扩展 NSAlert,添加一个便捷方法来显示警报 +extension NSAlert { + static func showAlert(title: String, message: String) { + let alert = NSAlert() + alert.messageText = title + alert.informativeText = message + alert.addButton(withTitle: "OK") + alert.runModal() + } +} + +// 用户指引界面 +struct GuideView: View { + var body: some View { + VStack(spacing: 20) { + // 标题 + VStack(spacing: 8) { + Image(systemName: "power") + .font(.system(size: 48)) + .foregroundColor(.blue) + Text("欢迎使用 PowerBar") + .font(.title) + .fontWeight(.bold) + Text("智能睡眠管理工具") + .font(.subheadline) + .foregroundColor(.secondary) + } + .padding(.top, 20) + + // 功能介绍 + ScrollView { + VStack(alignment: .leading, spacing: 16) { + // 功能说明 + Section { + HStack(spacing: 12) { + Image(systemName: "sun.max.fill") + .font(.title) + .foregroundColor(.green) + .frame(width: 40) + VStack(alignment: .leading) { + Text("智能睡眠管理") + .font(.headline) + Text("根据您的活动状态自动控制屏幕睡眠,活动时保持屏幕常亮,空闲时允许系统睡眠。") + .font(.body) + .foregroundColor(.secondary) + } + } + + HStack(spacing: 12) { + Image(systemName: "power.on.circle") + .font(.title) + .foregroundColor(.orange) + .frame(width: 40) + VStack(alignment: .leading) { + Text("开机自动启动") + .font(.headline) + Text("可以选择在开机时自动启动应用,无需手动打开。") + .font(.body) + .foregroundColor(.secondary) + } + } + } + + // 权限说明 + Section { + Text("所需权限") + .font(.title2) + .fontWeight(.bold) + + VStack(alignment: .leading, spacing: 8) { + Text("1. **自动启动权限**:用于实现开机自动启动功能") + Text("2. **系统空闲时间访问**:用于检测您的活动状态") + Text("3. **系统命令执行**:用于运行 caffeinate 命令阻止系统睡眠") + } + .font(.body) + .foregroundColor(.secondary) + } + + // 使用指引 + Section { + Text("使用指引") + .font(.title2) + .fontWeight(.bold) + + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("1.") + .fontWeight(.bold) + .frame(width: 20, alignment: .leading) + Text("点击菜单栏中的太阳图标打开菜单") + } + HStack { + Text("2.") + .fontWeight(.bold) + .frame(width: 20, alignment: .leading) + Text("点击\"开启常亮\"按钮启动智能睡眠管理") + } + HStack { + Text("3.") + .fontWeight(.bold) + .frame(width: 20, alignment: .leading) + Text("应用会根据您的活动状态自动调整睡眠设置") + } + HStack { + Text("4.") + .fontWeight(.bold) + .frame(width: 20, alignment: .leading) + Text("可以选择\"开机自动启动\"方便长期使用") + } + } + .font(.body) + } + + // 状态说明 + Section { + Text("状态说明") + .font(.title2) + .fontWeight(.bold) + + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 12) { + Image(systemName: "sun.max.fill") + .foregroundColor(.green) + Text("绿色太阳:功能已开启,系统处于活动状态") + } + HStack(spacing: 12) { + Image(systemName: "sun.max.fill") + .foregroundColor(.orange) + Text("橙色太阳:功能已开启,系统处于空闲状态") + } + HStack(spacing: 12) { + Image(systemName: "sun.max") + .foregroundColor(.gray) + Text("灰色太阳:功能已关闭") + } + } + .font(.body) + } + } + .padding(.horizontal, 20) + } + + // 关闭按钮 + Button(action: { + // 关闭指引界面 + NSApp.keyWindow?.close() + }) { + Text("开始使用") + .font(.headline) + .foregroundColor(.white) + .padding(.vertical, 12) + .padding(.horizontal, 32) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(.blue) + ) + } + .padding(.bottom, 20) + } + .frame(width: 400, height: 500) + .background( + RoundedRectangle(cornerRadius: 12) + .fill(Color.white) + ) + .shadow(radius: 10) + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..689d43d --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# PowerBar + +一个智能的macOS菜单栏应用,根据用户活动状态自动管理系统睡眠。 + +## 功能特性 + +- 📊 **智能睡眠管理**:根据用户活动状态自动控制系统睡眠 +- 🔍 **空闲检测**:5分钟无操作自动视为空闲,停止阻止睡眠 +- 🎯 **清晰的状态指示**:通过菜单栏图标直观显示当前状态 +- 🎨 **现代化UI**:简洁美观的菜单界面,易于操作 +- ⚡ **低资源消耗**:轻量级设计,几乎不占用系统资源 + +## 安装方法 + +### 方法1:直接运行 + +1. 下载并解压应用包 +2. 双击 `PowerBar.app` 即可运行 + +### 方法2:通过DMG安装 + +1. 下载 `PowerBar.dmg` 文件 +2. 双击打开DMG文件 +3. 将 `PowerBar.app` 拖放到 `Applications` 文件夹 +4. 从Launchpad或Applications文件夹启动应用 + +## 使用说明 + +1. **启动应用**:双击应用图标,菜单栏会出现太阳图标 +2. **查看状态**:点击太阳图标查看当前状态 +3. **切换功能**:点击"开启常亮"或"关闭常亮"按钮切换功能 +4. **退出应用**:点击"退出 PowerBar"退出应用 + +## 状态说明 + +- 🟢 **绿色太阳**:功能已开启,系统处于活动状态,显示屏受到保护 +- 🟡 **橙色太阳**:功能已开启,系统处于空闲状态 +- ⚪ **灰色太阳**:功能已关闭 + +## 技术细节 + +- 基于SwiftUI开发 +- 使用IOKit获取系统空闲时间 +- 使用caffeinate命令管理系统睡眠 +- 支持arm64和x86_64架构 + +## 系统要求 + +- macOS 13.0+ (Ventura) +- Apple Silicon 或 Intel 处理器 + +## 许可证 + +MIT License + +## 作者 + +Leo12025