Compare commits
5 Commits
6b645dedf0
...
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
aef0f74616
|
|||
|
886b460d5e
|
|||
|
27e98a527f
|
|||
|
230a2836a6
|
|||
|
3514f450c9
|
@@ -11,20 +11,12 @@ class AppState: ObservableObject {
|
||||
// 功能的总开关
|
||||
@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() {
|
||||
// 检查并设置初始的自动启动状态
|
||||
@@ -37,52 +29,11 @@ class AppState: ObservableObject {
|
||||
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 {
|
||||
// 从“空闲”变为“活动中”
|
||||
// 开启功能,直接启动caffeinate,不检查用户活动
|
||||
startCaffeinate()
|
||||
}
|
||||
} else {
|
||||
// 关闭功能,停止caffeinate
|
||||
stopCaffeinate()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +76,7 @@ class AppState: ObservableObject {
|
||||
// 简化实现,直接启动 caffeinate 进程
|
||||
caffeinateProcess = Process()
|
||||
caffeinateProcess?.executableURL = URL(fileURLWithPath: "/usr/bin/caffeinate")
|
||||
caffeinateProcess?.arguments = ["-d"] // -d 指示系统不要关闭显示屏
|
||||
caffeinateProcess?.arguments = ["-i", "-s", "-d"] // -i 防止用户不活动导致睡眠,-s 防止系统进入睡眠,-d 防止显示屏关闭
|
||||
|
||||
// 配置输出,避免产生不必要的输出
|
||||
let outputPipe = Pipe()
|
||||
@@ -155,14 +106,7 @@ class AppState: ObservableObject {
|
||||
print("已重置 caffeinate 进程引用")
|
||||
}
|
||||
|
||||
// MARK: - UI State Update
|
||||
|
||||
/// 在主线程更新用户空闲状态
|
||||
private func updateUserIdleStatus(_ isIdle: Bool) {
|
||||
DispatchQueue.main.async {
|
||||
self.isUserIdle = isIdle
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Launch at Login Management
|
||||
|
||||
@@ -252,7 +196,6 @@ class AppState: ObservableObject {
|
||||
|
||||
deinit {
|
||||
// 确保 App 退出时清理资源
|
||||
stopMonitoringTimer()
|
||||
stopCaffeinate()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,61 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_16x16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_16x16@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
|
||||
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
@@ -83,8 +83,6 @@ struct MenuContent: View {
|
||||
.buttonStyle(.plain)
|
||||
|
||||
Divider()
|
||||
.transition(.opacity)
|
||||
.animation(.easeInOut(duration: 0.3), value: appState.isFunctionEnabled)
|
||||
|
||||
// 状态显示
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
@@ -95,18 +93,10 @@ struct MenuContent: View {
|
||||
if appState.isFunctionEnabled {
|
||||
HStack(spacing: 6) {
|
||||
Circle()
|
||||
.fill(appState.isUserIdle ? Color.orange : Color.green)
|
||||
.fill(Color.green)
|
||||
.frame(width: 8, height: 8)
|
||||
.transition(.scale(scale: 0.5, anchor: .center))
|
||||
Text(appState.isUserIdle ? "空闲" : "活动中")
|
||||
Text("保护中")
|
||||
.font(.body)
|
||||
|
||||
// 附加显示 caffeinate 状态
|
||||
if !appState.isUserIdle {
|
||||
Text("(保护中)")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HStack(spacing: 6) {
|
||||
@@ -119,8 +109,7 @@ struct MenuContent: View {
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.transition(.opacity)
|
||||
.animation(.easeInOut(duration: 0.3), value: appState.isFunctionEnabled || appState.isUserIdle)
|
||||
|
||||
}
|
||||
.padding(8)
|
||||
.background(
|
||||
|
||||