Files
PowerBar/PowerBar/AppState.swift
leo12025 041a4bb911 feat: 添加开机自启动功能和用户指引界面
- 新增开机自启动功能,使用SMAppService实现
- 添加用户首次使用指引界面,包含功能介绍和权限说明
- 更新菜单栏选项,增加自启动开关和查看指南按钮
- 添加应用权限配置文件PowerBar.entitlements
- 创建README文档和.gitignore文件
2025-12-27 18:39:01 +08:00

202 lines
6.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Foundation
import Combine
import IOKit.pwr_mgt
import ServiceManagement
// MARK: - App State Manager
class AppState: ObservableObject {
// MARK: - Published Properties (UI )
//
@Published var isFunctionEnabled: Bool = false
//
@Published var isUserIdle: Bool = false
//
@Published var isLaunchAtLogin: Bool = false
//
private var monitorTimer: Timer?
private var caffeinateProcess: Process? // 使Process
//
private let idleThreshold: TimeInterval = 300 // 5
private let checkInterval: TimeInterval = 10 // 10
//
init() {
//
checkLaunchAtLoginStatus()
}
// MARK: - Public Methods
///
func toggleFunction() {
isFunctionEnabled.toggle()
if isFunctionEnabled {
//
performCheck()
startMonitoringTimer()
} else {
//
stopMonitoringTimer()
stopCaffeinate()
updateUserIdleStatus(false) //
}
}
// MARK: - Monitoring Logic
private func startMonitoringTimer() {
guard monitorTimer == nil else { return }
print("开始监控...")
// 使 weak self
monitorTimer = Timer.scheduledTimer(withTimeInterval: checkInterval, repeats: true) { [weak self] _ in
self?.performCheck()
}
}
private func stopMonitoringTimer() {
monitorTimer?.invalidate()
monitorTimer = nil
print("监控已停止。")
}
private func performCheck() {
// 1.
let idleTime = getSystemIdleTime()
// 2.
let isIdleNow = (idleTime >= idleThreshold)
// 3.
if isIdleNow != isUserIdle {
updateUserIdleStatus(isIdleNow)
if isIdleNow {
//
stopCaffeinate()
} else {
//
startCaffeinate()
}
}
}
// MARK: - User Idle Detection
/// 使 IOKit ()
/// - Returns:
private func getSystemIdleTime() -> TimeInterval {
//
var onPort: mach_port_t = 0
// IO Master Port
let kr = IOMainPort(0, &onPort)
guard kr == KERN_SUCCESS else { return 0 }
// HID HIDIdleTime
let hidSystem = IOServiceGetMatchingService(onPort, IOServiceMatching("IOHIDSystem"))
guard hidSystem != 0 else { return 0 }
//
let result = IORegistryEntryCreateCFProperty(hidSystem, "HIDIdleTime" as CFString, kCFAllocatorDefault, 0)
IOObjectRelease(hidSystem)
guard let data = result?.takeRetainedValue() as? Data else { return 0 }
// UInt64
var idleTime: UInt64 = 0
(data as NSData).getBytes(&idleTime, length: MemoryLayout<UInt64>.size)
return Double(idleTime) / 1_000_000_000.0 //
}
// MARK: - Caffeinate Management
private func startCaffeinate() {
guard caffeinateProcess == nil else { return }
print("用户活动,启动 caffeinate 阻止睡眠...")
// caffeinate
caffeinateProcess = Process()
caffeinateProcess?.executableURL = URL(fileURLWithPath: "/usr/bin/caffeinate")
caffeinateProcess?.arguments = ["-d"] // -d
//
let outputPipe = Pipe()
caffeinateProcess?.standardOutput = outputPipe
caffeinateProcess?.standardError = outputPipe
caffeinateProcess?.standardInput = Pipe()
do {
try caffeinateProcess?.run()
print("成功启动 caffeinate 进程")
} catch {
print("启动 caffeinate 失败: \(error)")
caffeinateProcess = nil
}
}
private func stopCaffeinate() {
guard let process = caffeinateProcess else { return }
print("用户空闲或功能关闭,停止 caffeinate 进程")
//
process.terminate()
// 退
caffeinateProcess = nil
print("已重置 caffeinate 进程引用")
}
// MARK: - UI State Update
/// 线
private func updateUserIdleStatus(_ isIdle: Bool) {
DispatchQueue.main.async {
self.isUserIdle = isIdle
}
}
// 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 {
// App 退
stopMonitoringTimer()
stopCaffeinate()
}
}