diff --git a/HemuLock/AppDelegate.swift b/HemuLock/AppDelegate.swift index 20b264f..b930866 100644 --- a/HemuLock/AppDelegate.swift +++ b/HemuLock/AppDelegate.swift @@ -371,6 +371,32 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele } observer = EventObserver() + + // Check for system launch event - only fire once per system boot + if SystemBootManager.shared.isNewSystemBoot() { + // Mark this boot as notified first to prevent duplicate notifications. + // This ensures the event fires only once per boot, even if the user + // enables/disables the event or relaunches the app during the same boot session. + SystemBootManager.shared.markBootNotified() + + // Only handle if System Launch event is enabled in config + if appState.appConfig.activeEvents.contains(Event.systemLaunch.tag) { + // Handle recording + if appState.appConfig.isRecordEvent { + observer?.recordEvent(event: .systemLaunch) + } + + // Send notification + if appState.appConfig.notifyType != Notify.none.tag { + observer?.sendNotify(event: .systemLaunch) + } + + // Execute script + if appState.appConfig.isExecScript { + observer?.runScript(Event.systemLaunch.name) + } + } + } } /** diff --git a/HemuLock/Base.lproj/Localizable.strings b/HemuLock/Base.lproj/Localizable.strings index 3e1ac4a..ae8dfeb 100644 --- a/HemuLock/Base.lproj/Localizable.strings +++ b/HemuLock/Base.lproj/Localizable.strings @@ -34,6 +34,7 @@ "SYSTEM_WAKE" = "System Wake"; "SYSTEM_LOCK" = "System Lock"; "SYSTEM_UNLOCK" = "System Unlock"; +"SYSTEM_LAUNCH" = "System Started"; "EVENT_SCREEN_SLEEP" = "Screen Sleep"; "EVENT_SCREEN_WAKE" = "Screen Wake"; @@ -41,6 +42,7 @@ "EVENT_SYSTEM_WAKE" = "System Wake"; "EVENT_SYSTEM_LOCK" = "System Lock"; "EVENT_SYSTEM_UNLOCK" = "System Unlock"; +"EVENT_SYSTEM_LAUNCH" = "System Launch"; "EMPTY_PUSHOVER" = "Pushover Configuration Parameters Error"; "EMPTY_BARK" = "Bark Configuration Parameters Error"; diff --git a/HemuLock/Enums/Event.swift b/HemuLock/Enums/Event.swift index af4d3d8..7d6268c 100644 --- a/HemuLock/Enums/Event.swift +++ b/HemuLock/Enums/Event.swift @@ -15,6 +15,7 @@ enum Event: String, CaseIterable { case systemWake = "SYSTEM_WAKE" case systemLock = "SYSTEM_LOCK" case systemUnLock = "SYSTEM_UNLOCK" + case systemLaunch = "SYSTEM_LAUNCH" var name: String { return rawValue @@ -34,6 +35,8 @@ enum Event: String, CaseIterable { return 130 case .systemUnLock: return 131 + case .systemLaunch: + return 140 } } @@ -51,6 +54,8 @@ enum Event: String, CaseIterable { return NSNotification.Name(rawValue: "com.apple.screenIsLocked") case .systemUnLock: return NSNotification.Name(rawValue: "com.apple.screenIsUnlocked") + case .systemLaunch: + return NSNotification.Name(rawValue: "com.hemulock.systemLaunch") } } } diff --git a/HemuLock/Managers/SystemBootManager.swift b/HemuLock/Managers/SystemBootManager.swift new file mode 100644 index 0000000..7e4269f --- /dev/null +++ b/HemuLock/Managers/SystemBootManager.swift @@ -0,0 +1,81 @@ +// +// SystemBootManager.swift +// HemuLock +// +// Created by HemuLock on 2024/02/09. +// + +import Foundation + +/** + SystemBootManager handles detection of system boot events. + + This manager tracks system boot time and determines if a notification + for the current boot has already been sent. It uses sysctl to get the + system boot time and UserDefaults to persist the last notified boot time. + */ +class SystemBootManager { + static let shared = SystemBootManager() + + private let lastBootTimeKey = "last_notified_boot_time" + + // Tolerance in seconds for comparing boot times. + // System boot times may have minor differences due to precision, + // so we use a 1-second tolerance to avoid false positives. + private let bootTimeToleranceSeconds: TimeInterval = 1.0 + + private init() {} + + /** + Get the current system boot time using sysctl. + + - Returns: The date when the system was last booted, or nil if unable to determine + */ + func getSystemBootTime() -> Date? { + var mib = [CTL_KERN, KERN_BOOTTIME] + var bootTime = timeval() + var size = MemoryLayout.stride + + if sysctl(&mib, UInt32(mib.count), &bootTime, &size, nil, 0) != -1 { + return Date(timeIntervalSince1970: TimeInterval(bootTime.tv_sec)) + } + return nil + } + + /** + Check if this is a new system boot (not just app relaunch). + + Compares the current system boot time with the last boot time that was notified. + + - Returns: true if this is a new boot that hasn't been notified yet, false otherwise + */ + func isNewSystemBoot() -> Bool { + guard let currentBootTime = getSystemBootTime() else { + // If we can't determine boot time, don't trigger the event + return false + } + + // Get the last notified boot time from UserDefaults + if let lastBootTime = UserDefaults.standard.object(forKey: lastBootTimeKey) as? Date { + // If the boot times are different, it's a new boot + // Allow a small tolerance for timing differences + return abs(currentBootTime.timeIntervalSince(lastBootTime)) > bootTimeToleranceSeconds + } + + // If no previous boot time is stored, consider it a new boot + return true + } + + /** + Mark the current boot as notified. + + Stores the current system boot time in UserDefaults to prevent + duplicate notifications for the same boot. + */ + func markBootNotified() { + guard let currentBootTime = getSystemBootTime() else { + return + } + UserDefaults.standard.set(currentBootTime, forKey: lastBootTimeKey) + } +} diff --git a/HemuLock/en.lproj/Localizable.strings b/HemuLock/en.lproj/Localizable.strings index ceb9931..ed07b6f 100644 --- a/HemuLock/en.lproj/Localizable.strings +++ b/HemuLock/en.lproj/Localizable.strings @@ -34,6 +34,7 @@ "SYSTEM_WAKE" = "System Wake"; "SYSTEM_LOCK" = "System Lock"; "SYSTEM_UNLOCK" = "System Unlock"; +"SYSTEM_LAUNCH" = "System Started"; "EVENT_SCREEN_SLEEP" = "Screen Sleep"; "EVENT_SCREEN_WAKE" = "Screen Wake"; @@ -41,6 +42,7 @@ "EVENT_SYSTEM_WAKE" = "System Wake"; "EVENT_SYSTEM_LOCK" = "System Lock"; "EVENT_SYSTEM_UNLOCK" = "System Unlock"; +"EVENT_SYSTEM_LAUNCH" = "System Launch"; "EMPTY_PUSHOVER" = "Pushover Configuration Parameters Error"; "EMPTY_BARK" = "Bark Configuration Parameters Error"; diff --git a/HemuLock/zh-Hans.lproj/Localizable.strings b/HemuLock/zh-Hans.lproj/Localizable.strings index 8be8c16..98e6742 100644 --- a/HemuLock/zh-Hans.lproj/Localizable.strings +++ b/HemuLock/zh-Hans.lproj/Localizable.strings @@ -37,6 +37,7 @@ "SYSTEM_WAKE" = "系统被唤醒"; "SYSTEM_LOCK" = "系统已被锁定"; "SYSTEM_UNLOCK" = "系统已解锁"; +"SYSTEM_LAUNCH" = "系统已启动"; "EVENT_SCREEN_SLEEP" = "屏幕睡眠"; "EVENT_SCREEN_WAKE" = "屏幕唤醒"; @@ -44,6 +45,7 @@ "EVENT_SYSTEM_WAKE" = "系统唤醒"; "EVENT_SYSTEM_LOCK" = "锁定"; "EVENT_SYSTEM_UNLOCK" = "解锁"; +"EVENT_SYSTEM_LAUNCH" = "系统启动"; "EMPTY_PUSHOVER" = "Pushover配置参数错误"; "EMPTY_BARK" = "Bark配置参数错误";