Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/Battery Time Remaining.xcodeproj/xcuserdata/
/Battery Time Remaining.xcodeproj/project.xcworkspace/
15 changes: 14 additions & 1 deletion Battery Time Remaining.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
2841C7F415C91CC100F4F15F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2841C7F315C91CC100F4F15F /* AppDelegate.m */; };
2841C7F715C91CC200F4F15F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2841C7F515C91CC200F4F15F /* MainMenu.xib */; };
2841C7FE15C91CEF00F4F15F /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2841C7FD15C91CEF00F4F15F /* IOKit.framework */; };
D58C7EF815CD7C6100C07E0C /* StartAtLoginHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = D58C7EF715CD7C6100C07E0C /* StartAtLoginHelper.m */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -31,6 +32,8 @@
2841C7F315C91CC100F4F15F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
2841C7F615C91CC200F4F15F /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
2841C7FD15C91CEF00F4F15F /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
D58C7EF615CD7C6100C07E0C /* StartAtLoginHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StartAtLoginHelper.h; sourceTree = "<group>"; };
D58C7EF715CD7C6100C07E0C /* StartAtLoginHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StartAtLoginHelper.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -49,7 +52,6 @@
2841C7D115C91CC100F4F15F = {
isa = PBXGroup;
children = (
2841C7FD15C91CEF00F4F15F /* IOKit.framework */,
2841C7E615C91CC100F4F15F /* Battery Time Remaining */,
2841C7DF15C91CC100F4F15F /* Frameworks */,
2841C7DD15C91CC100F4F15F /* Products */,
Expand All @@ -67,6 +69,7 @@
2841C7DF15C91CC100F4F15F /* Frameworks */ = {
isa = PBXGroup;
children = (
2841C7FD15C91CEF00F4F15F /* IOKit.framework */,
2841C7E015C91CC100F4F15F /* Cocoa.framework */,
2841C7E215C91CC100F4F15F /* Other Frameworks */,
);
Expand All @@ -88,6 +91,8 @@
children = (
2841C7F215C91CC100F4F15F /* AppDelegate.h */,
2841C7F315C91CC100F4F15F /* AppDelegate.m */,
D58C7EF615CD7C6100C07E0C /* StartAtLoginHelper.h */,
D58C7EF715CD7C6100C07E0C /* StartAtLoginHelper.m */,
2841C7F515C91CC200F4F15F /* MainMenu.xib */,
2841C7E715C91CC100F4F15F /* Supporting Files */,
);
Expand Down Expand Up @@ -172,6 +177,7 @@
files = (
2841C7ED15C91CC100F4F15F /* main.m in Sources */,
2841C7F415C91CC100F4F15F /* AppDelegate.m in Sources */,
D58C7EF815CD7C6100C07E0C /* StartAtLoginHelper.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -213,6 +219,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
Expand Down Expand Up @@ -241,6 +248,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application: Wrep";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
Expand All @@ -250,13 +258,15 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.8;
PROVISIONING_PROFILE = "";
SDKROOT = macosx;
};
name = Release;
};
2841C7FB15C91CC200F4F15F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Battery Time Remaining/Battery Time Remaining-Prefix.pch";
Expand All @@ -269,11 +279,13 @@
2841C7FC15C91CC200F4F15F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "Developer ID Application: Wrep";
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Battery Time Remaining/Battery Time Remaining-Prefix.pch";
INFOPLIST_FILE = "Battery Time Remaining/Battery Time Remaining-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
WRAPPER_EXTENSION = app;
};
name = Release;
Expand All @@ -297,6 +309,7 @@
2841C7FC15C91CC200F4F15F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
4 changes: 3 additions & 1 deletion Battery Time Remaining/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@

@interface AppDelegate : NSObject <NSApplicationDelegate>

- (NSString*)GetTimeRemainingText;
- (void)updateStatusItem;
- (NSImage *)getBatteryIconNamed:(NSString *)iconName;

@property (assign) IBOutlet NSWindow *window;
@property (strong) NSStatusItem *statusItem;
@property (strong) NSMenuItem *startupToggle;

@end
139 changes: 114 additions & 25 deletions Battery Time Remaining/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,138 @@
//

#import "AppDelegate.h"
#import "StartAtLoginHelper.h"
#import <IOKit/ps/IOPowerSources.h>
#import <IOKit/ps/IOPSKeys.h>

static void PowerSourceChanged(void * context) {

AppDelegate * self = (__bridge AppDelegate *)context;
[self.statusItem setTitle:[self GetTimeRemainingText]];
// IOPS notification callback on power source change
static void PowerSourceChanged(void * context)
{
// Update the time remaining text
AppDelegate *self = (__bridge AppDelegate *)context;
[self updateStatusItem];
}

@implementation AppDelegate

@synthesize statusItem;
@synthesize statusItem, startupToggle;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSMenu *stackMenu = [[NSMenu alloc] initWithTitle:@"Status Menu"];
[stackMenu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@""];
// Create the startup at login toggle
self.startupToggle = [[NSMenuItem alloc] initWithTitle:@"Start at login" action:@selector(toggleStartAtLogin:) keyEquivalent:@""];
self.startupToggle.target = self;
self.startupToggle.state = ([StartAtLoginHelper isInLoginItems]) ? NSOnState : NSOffState;

statusItem = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength];
[statusItem setHighlightMode:YES];
[statusItem setMenu:stackMenu];
// Build the status menu
NSMenu *statusMenu = [[NSMenu alloc] initWithTitle:@"Status Menu"];
[statusMenu addItem:self.startupToggle];
[statusMenu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@""];

NSString *title = [self GetTimeRemainingText];
// Create the status item and set initial text
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
statusItem.highlightMode = YES;
statusItem.menu = statusMenu;
[self updateStatusItem];

[statusItem setTitle:title];

// Update Power Source
// Capture Power Source updates and make sure our callback is called
CFRunLoopSourceRef loop = IOPSNotificationCreateRunLoopSource(PowerSourceChanged, (__bridge void *)self);
CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, kCFRunLoopDefaultMode);
CFRelease(loop);
}

- (NSString*)GetTimeRemainingText
- (void)updateStatusItem
{
// Get the estimated time remaining
CFTimeInterval timeRemaining = IOPSGetTimeRemainingEstimate();

// We're connected to an unlimited power source (AC adapter probably)
if (kIOPSTimeRemainingUnlimited == timeRemaining)
{
// Get list of power sources
CFTypeRef psBlob = IOPSCopyPowerSourcesInfo();
CFArrayRef psList = IOPSCopyPowerSourcesList(psBlob);

// Loop through the list of power sources
CFIndex count = CFArrayGetCount(psList);
for (CFIndex i = 0; i < count; i++)
{
CFTypeRef powersource = CFArrayGetValueAtIndex(psList, i);
CFDictionaryRef description = IOPSGetPowerSourceDescription(psBlob, powersource);

// Skip if not present or not a battery
if (CFDictionaryGetValue(description, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse || !CFStringCompare(CFDictionaryGetValue(description, CFSTR(kIOPSPowerSourceStateKey)), CFSTR(kIOPSBatteryPowerValue), 0))
{
continue;
}

// Check if the battery is charging atm
if (CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) == kCFBooleanTrue)
{
CFNumberRef timeToChargeNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToFullChargeKey));
int timeTilCharged = [(__bridge NSNumber *)timeToChargeNum intValue];

if (timeTilCharged > 0)
{
// Calculate the hour/minutes
NSInteger hour = (int)timeTilCharged / 3600;
NSInteger minute = (int)timeTilCharged % 3600;

// Return the time remaining string
self.statusItem.image = [self getBatteryIconNamed:@"BatteryCharging"];
self.statusItem.title = [NSString stringWithFormat:@" %ld:%02ld", hour, minute];
}
else
{
self.statusItem.image = [self getBatteryIconNamed:@"BatteryCharging"];
self.statusItem.title = @" Calculating…";
}
}
else
{
// Not charging and on a endless powersource
self.statusItem.image = [self getBatteryIconNamed:@"BatteryCharged"];
self.statusItem.title = @"";
}
}
}
// Still calculating the estimated time remaining...
else if (kIOPSTimeRemainingUnknown == timeRemaining)
{
self.statusItem.image = [self getBatteryIconNamed:@"BatteryEmpty"];
self.statusItem.title = @" Calculating…";
}
// Time is known!
else
{
// Calculate the hour/minutes
NSInteger hour = (int)timeRemaining / 3600;
NSInteger minute = (int)timeRemaining % 3600 / 60;

// Return the time remaining string
self.statusItem.image = [self getBatteryIconNamed:@"BatteryEmpty"];
self.statusItem.title = [NSString stringWithFormat:@" %ld:%02ld", hour, minute];
}
}

- (NSImage *)getBatteryIconNamed:(NSString *)iconName
{
if (kIOPSTimeRemainingUnlimited == IOPSGetTimeRemainingEstimate()) {
return @"Ω Unlimited Ω";
} else if (kIOPSTimeRemainingUnknown == IOPSGetTimeRemainingEstimate()) {
return @"Ω Calculating Ω";
} else {
CFTimeInterval time = IOPSGetTimeRemainingEstimate();
NSInteger hour = (int)time / 3600;
NSInteger minut = (int)time % 3600 / 60;
return [NSString stringWithFormat:@"<%ld:%02ld>", hour, minut];
NSString *fileName = [NSString stringWithFormat:@"/System/Library/CoreServices/Menu Extras/Battery.menu/Contents/Resources/%@.pdf", iconName];
return [[NSImage alloc] initWithContentsOfFile:fileName];
}

- (void)toggleStartAtLogin:(id)sender
{
// Check the state of start at login
if ([StartAtLoginHelper isInLoginItems])
{
[StartAtLoginHelper removeFromLoginItems];
self.startupToggle.state = NSOffState;
}
else
{
[StartAtLoginHelper addToLoginItems];
self.startupToggle.state = NSOnState;
}
}

Expand Down
2 changes: 1 addition & 1 deletion Battery Time Remaining/Battery Time Remaining-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2012 Han Lin Yap. All rights reserved.</string>
<string>Copyright © 2012 Han Lin Yap and Wrep. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
Expand Down
17 changes: 17 additions & 0 deletions Battery Time Remaining/StartAtLoginHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// StartAtLoginHelper.h
// Battery Time Remaining
//
// Created by Mathijs Kadijk on 04-08-12.
// Copyright (c) 2012 Wrep. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface StartAtLoginHelper : NSObject

+ (BOOL)isInLoginItems;
+ (void)addToLoginItems;
+ (void)removeFromLoginItems;

@end
Loading