diff --git a/BatteryEstimate.xcodeproj/project.pbxproj b/BatteryEstimate.xcodeproj/project.pbxproj
index 15fc436..2deaf9b 100644
--- a/BatteryEstimate.xcodeproj/project.pbxproj
+++ b/BatteryEstimate.xcodeproj/project.pbxproj
@@ -509,7 +509,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
- MARKETING_VERSION = 1.2;
+ MARKETING_VERSION = 1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.nafeeworkshop.BatteryEstimate;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -538,7 +538,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
- MARKETING_VERSION = 1.2;
+ MARKETING_VERSION = 1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.nafeeworkshop.BatteryEstimate;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/BatteryEstimate/AppDelegate.swift b/BatteryEstimate/AppDelegate.swift
index 38c1063..c1c0f91 100644
--- a/BatteryEstimate/AppDelegate.swift
+++ b/BatteryEstimate/AppDelegate.swift
@@ -14,9 +14,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
var updateTimer: Timer?
+ static var currentInterval = AppDelegate.updateInterval
var menu: NSMenu?
var windowController: MainWindowController?
-
+
+ //Power values
+ var isCharging: Bool!
+ var batteryPercentage: String!
+
+ //User Preferences
+ static let showPercentageKey: String = "ShowPercentage"
+ static var showPercentage: Bool = false
+ static let updateIntervalKey: String = "UpdateIntervalKey"
+ static var updateInterval: Double = 2
+
func applicationDidFinishLaunching(_ aNotification: Notification) {
//Create menu
@@ -25,13 +36,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
menu?.addItem(NSMenuItem(title: "Quit BatteryEstimate", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
statusItem.menu = menu
+ //load user preferences
+ loadPreferences()
+
//Update time remaining
- updateEstimate()
- updateTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(updateEstimate), userInfo: nil, repeats: true)
- RunLoop.current.add(updateTimer!, forMode: RunLoop.Mode.common)
+ updateAll()
//initialize window
windowController = (NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "mainWindow") as! MainWindowController)
+
+ updateTimer = Timer.scheduledTimer(timeInterval: AppDelegate.updateInterval, target: self, selector: #selector(updateAll), userInfo: nil, repeats: true)
+ RunLoop.current.add(updateTimer!, forMode: RunLoop.Mode.common)
}
func applicationWillTerminate(_ aNotification: Notification) {
@@ -39,15 +54,34 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
//Set menu bar title to current time remaining
- @objc func updateEstimate() {
+ @objc func updateAll() {
+ if AppDelegate.changeInterval() {
+ updateTimer?.invalidate()
+ updateTimer = Timer.scheduledTimer(timeInterval: AppDelegate.updateInterval, target: self, selector: #selector(updateAll), userInfo: nil, repeats: true)
+ RunLoop.current.add(updateTimer!, forMode: RunLoop.Mode.common)
+ AppDelegate.currentInterval = AppDelegate.updateInterval
+ }
- //⚡︎ official high voltage emoji
- //ϟ some symbol that looks like high voltage
- statusItem.button?.title = "⚡︎ " + getTimeRemaining()
+ updatePowerValues()
+ if (!AppDelegate.showPercentage) { statusItem.button?.title = getTimeRemaining() }
+ else { statusItem.button?.title = getTimeRemaining() + " | " + batteryPercentage}
+ }
+
+ static func changeInterval() -> Bool {
+ return AppDelegate.currentInterval != AppDelegate.updateInterval
+ }
+
+ func loadPreferences() {
+ if (UserDefaults.standard.value(forKey: AppDelegate.showPercentageKey) != nil) {
+ AppDelegate.showPercentage = UserDefaults.standard.value(forKey: AppDelegate.showPercentageKey) as! Bool
+ }
+ if (UserDefaults.standard.value(forKey: AppDelegate.updateIntervalKey) != nil) {
+ AppDelegate.updateInterval = UserDefaults.standard.value(forKey: AppDelegate.updateIntervalKey) as! Double
+ }
}
//returns esimated battery remaining as String in format HH:MM or other status
- @objc func getTimeRemaining() -> String {
+ func getTimeRemaining() -> String {
let remainingSeconds: Double = IOPSGetTimeRemainingEstimate()
//most likely condition so test first for optimization
@@ -55,23 +89,23 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let formattedHours: Int = Int(floor(remainingSeconds / 3600))
let formattedMinutes: Int = Int(remainingSeconds / 60) % 60
- if (formattedMinutes < 10) { return String(formattedHours) + ":" + "0" + String(formattedMinutes) }
+ if (formattedMinutes < 10) { return "↓ " + String(formattedHours) + ":" + "0" + String(formattedMinutes) }
- return String(formattedHours) + ":" + String(formattedMinutes)
+ return "↓ " + String(formattedHours) + ":" + String(formattedMinutes)
}
//if its not greater than 0 then check if its unknown, if so just say that its being calculated
else if (remainingSeconds == kIOPSTimeRemainingUnknown) { return "Calculating" }
//remainingSeconds fell through its checks so device must be plugged in, check if its charging and return so
- else if (isCharging()) { return "Charging" }
+ else if (isCharging) { return "⚡︎ Charging" } //ϟ
//fell through charging check, must not be charging
- return "Not Charging"
+ return "✗ Not Charging"
}
- //checks if the battery is charging
- func isCharging() -> Bool {
+ //updates power values
+ func updatePowerValues() {
let info = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let list = IOPSCopyPowerSourcesList(info).takeRetainedValue() as Array
@@ -79,12 +113,28 @@ class AppDelegate: NSObject, NSApplicationDelegate {
if let desc = IOPSGetPowerSourceDescription(info, ps).takeUnretainedValue() as? [String: Any] {
//if the powersource is the battery, return if it is charging or not
let psName = desc[kIOPSNameKey] as? String
- let isCharging = desc[kIOPSIsChargingKey] as? Bool
- if (psName != nil && psName == "InternalBattery-0" && isCharging != nil) { return isCharging! }
+
+ //if the power source is nil or isn't the internal battery then set error values for power values
+ if (psName != nil && psName == "InternalBattery-0") {
+
+ //update isCharging
+ let isCharging = desc[kIOPSIsChargingKey] as? Bool
+ if (isCharging != nil) { self.isCharging = isCharging! }
+ else { self.isCharging = false }
+
+ //only update batteryPercentage if the user wants the percentage
+ if (AppDelegate.showPercentage) {
+ let currentCapacity = desc[kIOPSCurrentCapacityKey] as! Double
+ let maxCapacity = desc[kIOPSMaxCapacityKey] as! Double
+ self.batteryPercentage = String(format: "%.0f", (currentCapacity / maxCapacity) * 100) + "%"
+ }
+ }
+ else {
+ self.isCharging = false
+ batteryPercentage = "Error"
+ }
}
}
-
- return false;
}
@objc func loadPrefsWindow() {
diff --git a/BatteryEstimate/Base.lproj/Main.storyboard b/BatteryEstimate/Base.lproj/Main.storyboard
index 297aa50..81f7e77 100644
--- a/BatteryEstimate/Base.lproj/Main.storyboard
+++ b/BatteryEstimate/Base.lproj/Main.storyboard
@@ -721,13 +721,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BatteryEstimate/ViewController.swift b/BatteryEstimate/ViewController.swift
index ee68ed6..f0049d8 100644
--- a/BatteryEstimate/ViewController.swift
+++ b/BatteryEstimate/ViewController.swift
@@ -33,5 +33,27 @@ class ViewController: NSViewController {
@IBAction func launchOnLoginClicked(_ checkbox: NSButton) {
LaunchAtLogin.isEnabled = (checkbox.state == NSButton.StateValue.on)
}
+
+ @IBOutlet var showPercentage: NSButton! {
+ didSet {
+ showPercentage.state = AppDelegate.showPercentage ? NSButton.StateValue.on : NSButton.StateValue.off
+ }
+ }
+
+ @IBAction func showPercentageClicked(_ checkbox: NSButton) {
+ AppDelegate.showPercentage = checkbox.state == NSButton.StateValue.on
+ UserDefaults.standard.set(checkbox.state == NSButton.StateValue.on, forKey: AppDelegate.showPercentageKey)
+ }
+
+ @IBOutlet var updateInterval: NSPopUpButton! {
+ didSet {
+ updateInterval.selectItem(at: Int(AppDelegate.updateInterval - 1))
+ }
+ }
+
+ @IBAction func updateIntervalClicked(_ menuItem: NSPopUpButton) {
+ AppDelegate.updateInterval = Double(menuItem.indexOfSelectedItem + 1)
+ UserDefaults.standard.set(AppDelegate.updateInterval, forKey: AppDelegate.updateIntervalKey)
+ }
}