Skip to content

Commit 016165c

Browse files
committed
Fix 1.1.1
1 parent 2c5fd47 commit 016165c

File tree

4 files changed

+127
-3
lines changed

4 files changed

+127
-3
lines changed

ChargeMonitor.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@
293293
"@executable_path/../Frameworks",
294294
);
295295
MACOSX_DEPLOYMENT_TARGET = 26.0;
296-
MARKETING_VERSION = 1.1.0;
296+
MARKETING_VERSION = 1.1.1;
297297
OTHER_LDFLAGS = (
298298
"$(inherited)",
299299
"-lsqlite3",
@@ -352,7 +352,7 @@
352352
"@executable_path/../Frameworks",
353353
);
354354
MACOSX_DEPLOYMENT_TARGET = 26.0;
355-
MARKETING_VERSION = 1.1.0;
355+
MARKETING_VERSION = 1.1.1;
356356
OTHER_LDFLAGS = (
357357
"$(inherited)",
358358
"-lsqlite3",
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Combine
2+
import Foundation
3+
4+
@MainActor
5+
final class AppBehaviorCoordinator: ObservableObject {
6+
private let settingEffects: [any SettingEffect]
7+
private var cancellables: Set<AnyCancellable> = []
8+
9+
convenience init(configurationManager: ConfigurationManager) {
10+
self.init(
11+
configurationManager: configurationManager,
12+
settingEffects: SettingEffectFactory.makeDefault()
13+
)
14+
}
15+
16+
init(configurationManager: ConfigurationManager, settingEffects: [any SettingEffect]) {
17+
self.settingEffects = settingEffects
18+
bind(to: configurationManager)
19+
}
20+
21+
private func bind(to configurationManager: ConfigurationManager) {
22+
applyConfiguration(configurationManager.configuration)
23+
24+
configurationManager.$configuration
25+
.sink { [weak self] config in
26+
self?.applyConfiguration(config)
27+
}
28+
.store(in: &cancellables)
29+
}
30+
31+
private func applyConfiguration(_ configuration: AppConfiguration) {
32+
for effect in settingEffects {
33+
effect.apply(isEnabled: configuration.enabledOptions.contains(effect.option))
34+
}
35+
}
36+
}

ChargeMonitor/Services/IOKitBatteryReader.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ struct IOKitBatteryReader {
5151
let isFastCharging = chargingByWire && chargingPower >= Self.fastChargePowerThresholdW
5252

5353
var snapshot = BatterySnapshot()
54-
snapshot.systemUptimeSeconds = ProcessInfo.processInfo.systemUptime
54+
snapshot.systemUptimeSeconds = Self.readUptimeSeconds()
5555
snapshot.powerSource = powerSource
5656
snapshot.isCharging = isCharging
5757
snapshot.isFastCharging = isFastCharging
@@ -256,6 +256,17 @@ struct IOKitBatteryReader {
256256
(value * 100).rounded() / 100
257257
}
258258

259+
private static func readUptimeSeconds() -> TimeInterval {
260+
var mib = [CTL_KERN, KERN_BOOTTIME]
261+
var bootTime = timeval()
262+
var size = MemoryLayout<timeval>.size
263+
guard sysctl(&mib, 2, &bootTime, &size, nil, 0) == 0 else {
264+
return ProcessInfo.processInfo.systemUptime
265+
}
266+
let bootDate = Date(timeIntervalSince1970: TimeInterval(bootTime.tv_sec) + TimeInterval(bootTime.tv_usec) / 1_000_000)
267+
return Date().timeIntervalSince(bootDate)
268+
}
269+
259270
private func isFull(_ soc: Int?) -> Bool {
260271
guard let soc, (0...100).contains(soc) else { return false }
261272
return soc >= 100
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import Foundation
2+
3+
@MainActor
4+
protocol SettingEffect {
5+
var option: DisplayOption { get }
6+
func apply(isEnabled: Bool)
7+
}
8+
9+
@MainActor
10+
final class StartAtLoginSettingEffect: SettingEffect {
11+
let option: DisplayOption = .startAtLogin
12+
13+
private let loginItemController: LoginItemController
14+
private var appliedValue: Bool?
15+
16+
init(loginItemController: LoginItemController) {
17+
self.loginItemController = loginItemController
18+
}
19+
20+
@MainActor
21+
convenience init() {
22+
self.init(loginItemController: StartAtLoginSettingEffect.makeDefaultLoginItemController())
23+
}
24+
25+
private static func makeDefaultLoginItemController() -> LoginItemController {
26+
LoginItemController()
27+
}
28+
29+
func apply(isEnabled: Bool) {
30+
guard appliedValue != isEnabled else { return }
31+
appliedValue = isEnabled
32+
33+
do {
34+
try loginItemController.setEnabled(isEnabled)
35+
} catch {
36+
NSLog("Failed to update Start at Login: \(error)")
37+
}
38+
}
39+
}
40+
41+
@MainActor
42+
final class PreventSleepingSettingEffect: SettingEffect {
43+
let option: DisplayOption = .preventSleeping
44+
45+
private let sleepController: SleepController
46+
private var appliedValue: Bool?
47+
48+
init(sleepController: SleepController) {
49+
self.sleepController = sleepController
50+
}
51+
52+
@MainActor
53+
convenience init() {
54+
self.init(sleepController: PreventSleepingSettingEffect.makeDefaultSleepController())
55+
}
56+
57+
private static func makeDefaultSleepController() -> SleepController {
58+
SleepController()
59+
}
60+
61+
func apply(isEnabled: Bool) {
62+
guard appliedValue != isEnabled else { return }
63+
appliedValue = isEnabled
64+
sleepController.setPreventSleepEnabled(isEnabled)
65+
}
66+
}
67+
68+
enum SettingEffectFactory {
69+
@MainActor
70+
static func makeDefault() -> [any SettingEffect] {
71+
[
72+
StartAtLoginSettingEffect(),
73+
PreventSleepingSettingEffect()
74+
]
75+
}
76+
}
77+

0 commit comments

Comments
 (0)