Skip to content

Commit

Permalink
Notify user on low power situations (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianmichel authored Jan 2, 2017
1 parent ee4caec commit c2e5259
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 11 deletions.
39 changes: 30 additions & 9 deletions Juice.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
725AA5E01E05DBDB006E5DDE /* PreferencesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 725AA5DF1E05DBDB006E5DDE /* PreferencesCoordinator.swift */; };
725AA5E51E05DE22006E5DDE /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 725AA5E31E05DE22006E5DDE /* PreferencesWindowController.swift */; };
725AA5E61E05DE22006E5DDE /* PreferencesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 725AA5E41E05DE22006E5DDE /* PreferencesWindowController.xib */; };
728C64581E1AB52F008DA4A6 /* LowPowerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 728C64571E1AB52F008DA4A6 /* LowPowerCoordinator.swift */; };
728EB92E1E0601D800920B83 /* ChargeScaleDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 728EB92D1E0601D800920B83 /* ChargeScaleDisplay.swift */; };
728EB9311E06033300920B83 /* PowerSource+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 728EB9301E06033300920B83 /* PowerSource+Extensions.swift */; };
728EB9341E0605CB00920B83 /* FileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 728EB9331E0605CB00920B83 /* FileManager+Extensions.swift */; };
Expand Down Expand Up @@ -73,6 +74,7 @@
725AA5DF1E05DBDB006E5DDE /* PreferencesCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferencesCoordinator.swift; path = Classes/PreferencesCoordinator.swift; sourceTree = "<group>"; };
725AA5E31E05DE22006E5DDE /* PreferencesWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferencesWindowController.swift; path = Classes/PreferencesWindowController.swift; sourceTree = "<group>"; };
725AA5E41E05DE22006E5DDE /* PreferencesWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PreferencesWindowController.xib; path = Classes/PreferencesWindowController.xib; sourceTree = "<group>"; };
728C64571E1AB52F008DA4A6 /* LowPowerCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LowPowerCoordinator.swift; path = Classes/LowPowerCoordinator.swift; sourceTree = "<group>"; };
728EB92D1E0601D800920B83 /* ChargeScaleDisplay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChargeScaleDisplay.swift; path = Classes/ChargeScaleDisplay.swift; sourceTree = "<group>"; };
728EB9301E06033300920B83 /* PowerSource+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PowerSource+Extensions.swift"; path = "Classes/PowerSource+Extensions.swift"; sourceTree = "<group>"; };
728EB9321E0604D400920B83 /* Juice.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Juice.entitlements; sourceTree = "<group>"; };
Expand Down Expand Up @@ -138,7 +140,6 @@
725AA5B31E05BF3A006E5DDE = {
isa = PBXGroup;
children = (
AAA3A2A51E0F3F0F0015165E /* Localizable.strings */,
72DA73671E09FEA7003F9262 /* Shared */,
725AA5BE1E05BF3A006E5DDE /* Juice */,
72DA735B1E09FE19003F9262 /* JuiceHelper */,
Expand All @@ -160,16 +161,14 @@
725AA5BE1E05BF3A006E5DDE /* Juice */ = {
isa = PBXGroup;
children = (
728EB9321E0604D400920B83 /* Juice.entitlements */,
728C64591E1ABA35008DA4A6 /* Resources */,
728EB92F1E06032600920B83 /* Extensions */,
728EB92C1E0601A700920B83 /* Display String Parsing */,
728C64561E1AB51D008DA4A6 /* Low Power */,
725AA5DE1E05DBCD006E5DDE /* Preferences */,
725AA5D11E05C18A006E5DDE /* Main Application Hierarchy */,
725AA5CC1E05C083006E5DDE /* Power Source */,
725AA5BF1E05BF3A006E5DDE /* AppDelegate.swift */,
725AA5C11E05BF3A006E5DDE /* Assets.xcassets */,
725AA5C31E05BF3A006E5DDE /* MainMenu.xib */,
725AA5C61E05BF3A006E5DDE /* Info.plist */,
);
path = Juice;
sourceTree = "<group>";
Expand Down Expand Up @@ -215,6 +214,26 @@
name = Preferences;
sourceTree = "<group>";
};
728C64561E1AB51D008DA4A6 /* Low Power */ = {
isa = PBXGroup;
children = (
728C64571E1AB52F008DA4A6 /* LowPowerCoordinator.swift */,
);
name = "Low Power";
sourceTree = "<group>";
};
728C64591E1ABA35008DA4A6 /* Resources */ = {
isa = PBXGroup;
children = (
725AA5C61E05BF3A006E5DDE /* Info.plist */,
725AA5C31E05BF3A006E5DDE /* MainMenu.xib */,
725AA5C11E05BF3A006E5DDE /* Assets.xcassets */,
AAA3A2A51E0F3F0F0015165E /* Localizable.strings */,
728EB9321E0604D400920B83 /* Juice.entitlements */,
);
name = Resources;
sourceTree = "<group>";
};
728EB92C1E0601A700920B83 /* Display String Parsing */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -462,6 +481,7 @@
725AA5C01E05BF3A006E5DDE /* AppDelegate.swift in Sources */,
725AA5D31E05C19A006E5DDE /* ApplicationCoordinator.swift in Sources */,
725AA5E01E05DBDB006E5DDE /* PreferencesCoordinator.swift in Sources */,
728C64581E1AB52F008DA4A6 /* LowPowerCoordinator.swift in Sources */,
725AA5E51E05DE22006E5DDE /* PreferencesWindowController.swift in Sources */,
728EB93A1E061B4700920B83 /* PreferencesStorage.swift in Sources */,
725AA5DD1E05D3CD006E5DDE /* StatusItemMenu.swift in Sources */,
Expand Down Expand Up @@ -510,6 +530,7 @@
AAA3A2A71E0F3F150015165E /* fr */,
);
name = Localizable.strings;
path = ..;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
Expand Down Expand Up @@ -617,7 +638,7 @@
CODE_SIGN_ENTITLEMENTS = Juice/Juice.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 529;
CURRENT_PROJECT_VERSION = 2017.01.02105829;
DEVELOPMENT_TEAM = YN24FFRTC8;
INFOPLIST_FILE = Juice/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
Expand All @@ -635,7 +656,7 @@
CODE_SIGN_ENTITLEMENTS = Juice/Juice.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 529;
CURRENT_PROJECT_VERSION = 2017.01.02105829;
DEVELOPMENT_TEAM = YN24FFRTC8;
INFOPLIST_FILE = Juice/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
Expand All @@ -652,7 +673,7 @@
CODE_SIGN_ENTITLEMENTS = JuiceHelper/JuiceHelper.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 529;
CURRENT_PROJECT_VERSION = 2017.01.02105829;
DEVELOPMENT_TEAM = YN24FFRTC8;
INFOPLIST_FILE = JuiceHelper/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
Expand All @@ -670,7 +691,7 @@
CODE_SIGN_ENTITLEMENTS = JuiceHelper/JuiceHelper.entitlements;
CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 529;
CURRENT_PROJECT_VERSION = 2017.01.02105829;
DEVELOPMENT_TEAM = YN24FFRTC8;
INFOPLIST_FILE = JuiceHelper/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
Expand Down
76 changes: 76 additions & 0 deletions Juice/Classes/LowPowerCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// LowPowerCoordinator.swift
// Juice
//
// Created by Brian Michel on 1/2/17.
// Copyright © 2017 Brian Michel. All rights reserved.
//

import AppKit
import RxSwift

final class LowPowerCoordinator {
private enum Constants {
//TODO: Could probably make this a user preference
static let lowPowerNotificationPercentage = 10
static let lowPowerNotificationIdentifier = "com.bsm.macos.juice.low-power-notification"
}
private let powerSourceObservable: Observable<[PowerSource]>
private let notificationCenter = NSUserNotificationCenter.default
private let disposeBag = DisposeBag()
private var lastPowerState: PowerSourceState = .unknown

private var notificationHasBeenDelivered: Bool {
return notificationCenter.deliveredNotifications.filter({ $0.identifier == Constants.lowPowerNotificationIdentifier }).count > 0
}

init(observable: Observable<[PowerSource]>) {
powerSourceObservable = observable
}

func start() {
powerSourceObservable.shareReplay(1).subscribe { (event) in
switch event {
case .next(let sources):
guard let source = sources.first else {
return
}
self.checkForNotificationDismissal(powerSource: source)
self.checkForWarning(powerSource: source)
case .error(let error):
print("Error: \(error)")
case .completed:
break
}
}.addDisposableTo(disposeBag)
}

private func checkForNotificationDismissal(powerSource: PowerSource) {
if lastPowerState == .battery
&& powerSource.state == .ac {
clearDeliveredNotification()
}

lastPowerState = powerSource.state
}

private func checkForWarning(powerSource: PowerSource) {
if powerSource.chargedPercentage <= Constants.lowPowerNotificationPercentage
&& powerSource.state == .battery
&& !notificationHasBeenDelivered {
let userNotification = NSUserNotification()
userNotification.title = NSLocalizedString("Low Power", comment: "Title of notification shown to user when their battery is very low.")
userNotification.informativeText = NSLocalizedString("Your Mac will sleep soon unless plugged into a power outlet.", comment: "Body of notification shown to user when their battery is very low.")
userNotification.hasActionButton = false
userNotification.identifier = Constants.lowPowerNotificationIdentifier

notificationCenter.deliver(userNotification)
}
}

private func clearDeliveredNotification() {
let userNotification = NSUserNotification()
userNotification.identifier = Constants.lowPowerNotificationIdentifier
notificationCenter.removeDeliveredNotification(userNotification)
}
}
5 changes: 5 additions & 0 deletions Juice/Classes/StatusBarItemCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import RxSwift

final class StatusBarItemCoordinator: StatusMenuItemDelegate {
private let preferencesCoordinator = PreferencesCoordinator()
private let lowPowerCoordinator: LowPowerCoordinator

private let statusBarItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)
private let statusBarItemMenu = StatusMenuItem(title: "Juice")
Expand All @@ -35,6 +36,8 @@ final class StatusBarItemCoordinator: StatusMenuItemDelegate {

return disposable
})

lowPowerCoordinator = LowPowerCoordinator(observable: powerSourcesObservable)

scaleChangeObservable = preferencesStorage
.chargeDisplayScale
Expand All @@ -56,6 +59,8 @@ final class StatusBarItemCoordinator: StatusMenuItemDelegate {
scaleChangeObservable.subscribe(onNext: { (scale) in
self.updateLabel(scale: scale)
}).addDisposableTo(disposableBag)

lowPowerCoordinator.start()
}

private func updateLabel(source: PowerSource) {
Expand Down
4 changes: 3 additions & 1 deletion Juice/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>529</string>
<string>2017.01.02105829</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.lifestyle</string>
<key>LSMinimumSystemVersion</key>
Expand All @@ -32,5 +32,7 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSUserNotificationAlertStyle</key>
<string>alert</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion JuiceHelper/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>529</string>
<string>2017.01.02105829</string>
<key>LSBackgroundOnly</key>
<true/>
<key>LSMinimumSystemVersion</key>
Expand Down
2 changes: 2 additions & 0 deletions en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@
"Unknown Build"="Unknown Build";
"Credits for this app"="Credits for this app";
"Indicates the current battery charge percentage"="Indicates the current battery charge percentage";
"Low Power"="Low Power";
"Your Mac will sleep soon unless plugged into a power outlet."="Your Mac will sleep soon unless plugged into a power outlet.";

0 comments on commit c2e5259

Please sign in to comment.