Skip to content

Commit

Permalink
Merge pull request #180 from freepicheep/lowpower-battery
Browse files Browse the repository at this point in the history
Support for low power mode battery visual
  • Loading branch information
richardkunkli authored Nov 1, 2024
2 parents a6d0cca + 665a725 commit dcd9217
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 19 deletions.
3 changes: 2 additions & 1 deletion boringNotch/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ struct ContentView: View {
HStack {
BoringBatteryView(
batteryPercentage: batteryModel.batteryPercentage, isPluggedIn: batteryModel.isPluggedIn,
batteryWidth: 30
batteryWidth: 30,
isInLowPowerMode: batteryModel.isInLowPowerMode
)
}
.frame(width: 76, alignment: .trailing)
Expand Down
11 changes: 8 additions & 3 deletions boringNotch/components/Live activities/BoringBattery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import SwiftUI
struct BatteryView: View {
@State var percentage: Float
@State var isCharging: Bool
@State var isInLowPowerMode: Bool
var batteryWidth: CGFloat = 26
var animationStyle: BoringAnimations = BoringAnimations()

Expand All @@ -11,7 +12,9 @@ struct BatteryView: View {
}

var batteryColor: Color {
if percentage.isLessThanOrEqualTo(20) {
if isInLowPowerMode {
return .yellow
} else if percentage.isLessThanOrEqualTo(20) {
return .red
} else if isCharging {
return .green
Expand Down Expand Up @@ -52,13 +55,14 @@ struct BoringBatteryView: View {
@State var batteryPercentage: Float = 0
@State var isPluggedIn:Bool = false
@State var batteryWidth: CGFloat = 26
@State var isInLowPowerMode: Bool

var body: some View {
HStack {
Text("\(Int32(batteryPercentage))%")
.font(.callout)
.foregroundStyle(.white)
BatteryView(percentage: batteryPercentage, isCharging: isPluggedIn, batteryWidth: batteryWidth)
BatteryView(percentage: batteryPercentage, isCharging: isPluggedIn, isInLowPowerMode: isInLowPowerMode, batteryWidth: batteryWidth)
}

}
Expand All @@ -68,6 +72,7 @@ struct BoringBatteryView: View {
BoringBatteryView(
batteryPercentage: 40,
isPluggedIn: true,
batteryWidth: 30
batteryWidth: 30,
isInLowPowerMode: false
).frame(width: 200, height: 200)
}
4 changes: 3 additions & 1 deletion boringNotch/components/Notch/BoringHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ struct BoringHeader: View {
if Defaults[.showBattery] {
BoringBatteryView(
batteryPercentage: batteryModel.batteryPercentage,
isPluggedIn: batteryModel.isPluggedIn, batteryWidth: 30)
isPluggedIn: batteryModel.isPluggedIn, batteryWidth: 30,
isInLowPowerMode: batteryModel.isInLowPowerMode
)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion boringNotch/components/Notch/NotchContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ struct NotchContentView: View {

if vm.notchState == .closed && vm.expandingView.show {
if vm.expandingView.type == .battery {
BoringBatteryView(batteryPercentage: batteryModel.batteryPercentage, isPluggedIn: batteryModel.isPluggedIn, batteryWidth: 30)
BoringBatteryView(batteryPercentage: batteryModel.batteryPercentage, isPluggedIn: batteryModel.isPluggedIn, batteryWidth: 30, isInLowPowerMode: batteryModel.isInLowPowerMode)
} else {
ProgressIndicator(type: .text, progress: 0.01, color: Defaults[.accentColor]).padding(.trailing, 4)
}
Expand Down
33 changes: 20 additions & 13 deletions boringNotch/models/BatteryStatusViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@ class BatteryStatusViewModel: ObservableObject {
@Published var batteryPercentage: Float = 0.0
@Published var isPluggedIn: Bool = false
@Published var showChargingInfo: Bool = false

@Published var isInLowPowerMode: Bool = false

private var powerSourceChangedCallback: IOPowerSourceCallbackType?
private var runLoopSource: Unmanaged<CFRunLoopSource>?
var animations: BoringAnimations = BoringAnimations()

init(vm: BoringViewModel) {
self.vm = vm
updateBatteryStatus()
startMonitoring()
// get the system power mode and setup an observer
self.isInLowPowerMode = ProcessInfo.processInfo.isLowPowerModeEnabled
NotificationCenter.default.addObserver(self, selector: #selector(powerStateChanged), name: Notification.Name.NSProcessInfoPowerStateDidChange, object: nil)
}

private func updateBatteryStatus() {
if let snapshot = IOPSCopyPowerSourcesInfo()?.takeRetainedValue(),
let sources = IOPSCopyPowerSourcesList(snapshot)?.takeRetainedValue() as? [CFTypeRef] {
Expand All @@ -35,36 +39,32 @@ class BatteryStatusViewModel: ObservableObject {
let currentCapacity = info[kIOPSCurrentCapacityKey] as? Int,
let maxCapacity = info[kIOPSMaxCapacityKey] as? Int,
let isCharging = info["Is Charging"] as? Bool {


if(Defaults[.chargingInfoAllowed]) {

withAnimation {
self.batteryPercentage = Float((currentCapacity * 100) / maxCapacity)
}

if (isCharging && !self.isPluggedIn) {
DispatchQueue.main.asyncAfter(deadline: .now() + (vm.firstLaunch ? 6 : 0)) {
self.vm.toggleExpandingView(status: true, type: .battery)
self.showChargingInfo = true
self.isPluggedIn = true
}

}
withAnimation {
self.isPluggedIn = isCharging
}

}

}
}
}
}

private func startMonitoring() {
let context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())

powerSourceChangedCallback = { context in
if let context = context {
let mySelf = Unmanaged<BatteryStatusViewModel>.fromOpaque(context).takeUnretainedValue()
Expand All @@ -73,13 +73,20 @@ class BatteryStatusViewModel: ObservableObject {
}
}
}

if let runLoopSource = IOPSNotificationCreateRunLoopSource(powerSourceChangedCallback!, context)?.takeRetainedValue() {
self.runLoopSource = Unmanaged<CFRunLoopSource>.passRetained(runLoopSource)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .defaultMode)
}
}

// function to update battery model if system low power mode changes
@objc func powerStateChanged(_ notification: Notification) {
DispatchQueue.main.async {
self.isInLowPowerMode = ProcessInfo.processInfo.isLowPowerModeEnabled
}
}

deinit {
if let runLoopSource = runLoopSource {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource.takeUnretainedValue(), .defaultMode)
Expand Down

0 comments on commit dcd9217

Please sign in to comment.