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 isFunctionEnabled: Bool = false
|
||||||
|
|
||||||
// 用户当前是否空闲
|
|
||||||
@Published var isUserIdle: Bool = false
|
|
||||||
|
|
||||||
// 自动启动设置
|
// 自动启动设置
|
||||||
@Published var isLaunchAtLogin: Bool = false
|
@Published var isLaunchAtLogin: Bool = false
|
||||||
|
|
||||||
// 内部属性
|
// 内部属性
|
||||||
private var monitorTimer: Timer?
|
|
||||||
private var caffeinateProcess: Process? // 直接使用Process对象,但简化实现
|
private var caffeinateProcess: Process? // 直接使用Process对象,但简化实现
|
||||||
|
|
||||||
// 配置参数
|
|
||||||
private let idleThreshold: TimeInterval = 300 // 5分钟无操作视为空闲
|
|
||||||
private let checkInterval: TimeInterval = 10 // 每10秒检查一次
|
|
||||||
|
|
||||||
// 初始化方法
|
// 初始化方法
|
||||||
init() {
|
init() {
|
||||||
// 检查并设置初始的自动启动状态
|
// 检查并设置初始的自动启动状态
|
||||||
@@ -37,52 +29,11 @@ class AppState: ObservableObject {
|
|||||||
func toggleFunction() {
|
func toggleFunction() {
|
||||||
isFunctionEnabled.toggle()
|
isFunctionEnabled.toggle()
|
||||||
if isFunctionEnabled {
|
if isFunctionEnabled {
|
||||||
// 开启功能,立即开始一轮检查,并设置定时器
|
// 开启功能,直接启动caffeinate,不检查用户活动
|
||||||
performCheck()
|
startCaffeinate()
|
||||||
startMonitoringTimer()
|
|
||||||
} else {
|
} else {
|
||||||
// 关闭功能,停止一切
|
// 关闭功能,停止caffeinate
|
||||||
stopMonitoringTimer()
|
|
||||||
stopCaffeinate()
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +76,7 @@ class AppState: ObservableObject {
|
|||||||
// 简化实现,直接启动 caffeinate 进程
|
// 简化实现,直接启动 caffeinate 进程
|
||||||
caffeinateProcess = Process()
|
caffeinateProcess = Process()
|
||||||
caffeinateProcess?.executableURL = URL(fileURLWithPath: "/usr/bin/caffeinate")
|
caffeinateProcess?.executableURL = URL(fileURLWithPath: "/usr/bin/caffeinate")
|
||||||
caffeinateProcess?.arguments = ["-d"] // -d 指示系统不要关闭显示屏
|
caffeinateProcess?.arguments = ["-i", "-s", "-d"] // -i 防止用户不活动导致睡眠,-s 防止系统进入睡眠,-d 防止显示屏关闭
|
||||||
|
|
||||||
// 配置输出,避免产生不必要的输出
|
// 配置输出,避免产生不必要的输出
|
||||||
let outputPipe = Pipe()
|
let outputPipe = Pipe()
|
||||||
@@ -155,14 +106,7 @@ class AppState: ObservableObject {
|
|||||||
print("已重置 caffeinate 进程引用")
|
print("已重置 caffeinate 进程引用")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UI State Update
|
|
||||||
|
|
||||||
/// 在主线程更新用户空闲状态
|
|
||||||
private func updateUserIdleStatus(_ isIdle: Bool) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.isUserIdle = isIdle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Launch at Login Management
|
// MARK: - Launch at Login Management
|
||||||
|
|
||||||
@@ -252,7 +196,6 @@ class AppState: ObservableObject {
|
|||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
// 确保 App 退出时清理资源
|
// 确保 App 退出时清理资源
|
||||||
stopMonitoringTimer()
|
|
||||||
stopCaffeinate()
|
stopCaffeinate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,61 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_16x16.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "16x16"
|
"size" : "16x16"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_16x16@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "16x16"
|
"size" : "16x16"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_32x32.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "32x32"
|
"size" : "32x32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_32x32@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "32x32"
|
"size" : "32x32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_128x128.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "128x128"
|
"size" : "128x128"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_128x128@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "128x128"
|
"size" : "128x128"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_256x256.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "256x256"
|
"size" : "256x256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_256x256@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "256x256"
|
"size" : "256x256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_512x512.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "512x512"
|
"size" : "512x512"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_512x512@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "512x512"
|
"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)
|
.buttonStyle(.plain)
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
.transition(.opacity)
|
|
||||||
.animation(.easeInOut(duration: 0.3), value: appState.isFunctionEnabled)
|
|
||||||
|
|
||||||
// 状态显示
|
// 状态显示
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
@@ -95,18 +93,10 @@ struct MenuContent: View {
|
|||||||
if appState.isFunctionEnabled {
|
if appState.isFunctionEnabled {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
Circle()
|
Circle()
|
||||||
.fill(appState.isUserIdle ? Color.orange : Color.green)
|
.fill(Color.green)
|
||||||
.frame(width: 8, height: 8)
|
.frame(width: 8, height: 8)
|
||||||
.transition(.scale(scale: 0.5, anchor: .center))
|
Text("保护中")
|
||||||
Text(appState.isUserIdle ? "空闲" : "活动中")
|
|
||||||
.font(.body)
|
.font(.body)
|
||||||
|
|
||||||
// 附加显示 caffeinate 状态
|
|
||||||
if !appState.isUserIdle {
|
|
||||||
Text("(保护中)")
|
|
||||||
.font(.caption2)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
@@ -119,8 +109,7 @@ struct MenuContent: View {
|
|||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.transition(.opacity)
|
|
||||||
.animation(.easeInOut(duration: 0.3), value: appState.isFunctionEnabled || appState.isUserIdle)
|
|
||||||
}
|
}
|
||||||
.padding(8)
|
.padding(8)
|
||||||
.background(
|
.background(
|
||||||
|
|||||||