├── .gitignore ├── icon.icns ├── preview.png ├── preview_advanced.png ├── Battery Time Remaining ├── de.lproj │ └── Localizable.strings ├── en.lproj │ ├── Localizable.strings │ └── Credits.rtf ├── es.lproj │ └── Localizable.strings ├── fr.lproj │ └── Localizable.strings ├── it.lproj │ └── Localizable.strings ├── ko.lproj │ └── Localizable.strings ├── nl.lproj │ └── Localizable.strings ├── pl.lproj │ └── Localizable.strings ├── pt.lproj │ └── Localizable.strings ├── ru.lproj │ ├── Localizable.strings │ └── Credits.rtf ├── sk.lproj │ └── Localizable.strings ├── sv.lproj │ └── Localizable.strings ├── zh-Hans.lproj │ └── Localizable.strings ├── zh-Hant-TW.lproj │ ├── Localizable.strings │ └── Credits.rtf ├── Battery Time Remaining-Prefix.pch ├── Battery Time Remaining.entitlements ├── ImageFilter.h ├── main.m ├── HttpGet.h ├── AppDelegate.h ├── ImageFilter.m ├── Battery Time Remaining-Info.plist ├── HttpGet.m └── AppDelegate.m ├── LaunchAtLoginHelper ├── LLStrings.h ├── LaunchAtLoginHelper │ ├── LaunchAtLoginHelper.entitlements │ ├── LLHAppDelegate.h │ ├── main.m │ ├── LLHAppDelegate.m │ ├── MainMenu.xib │ ├── LaunchAtLoginHelper-Info.plist │ └── LaunchAtLoginHelper-InfoBase.plist ├── LLManager.h ├── setup.py ├── LLManager.m ├── readme.md └── LaunchAtLoginHelper.xcodeproj │ └── project.pbxproj ├── LICENSE ├── Readme.md └── Battery Time Remaining.xcodeproj └── project.pbxproj /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcworkspace 2 | !default.xcworkspace 3 | xcuserdata 4 | 5 | .DS_Store -------------------------------------------------------------------------------- /icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/icon.icns -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/preview.png -------------------------------------------------------------------------------- /preview_advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/preview_advanced.png -------------------------------------------------------------------------------- /Battery Time Remaining/de.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/de.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/en.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/es.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/es.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/fr.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/fr.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/it.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/it.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/ko.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/ko.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/nl.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/nl.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/pl.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/pl.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/pt.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/pt.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/ru.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/ru.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/sk.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/sk.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/sv.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/sv.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/zh-Hans.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/zh-Hans.lproj/Localizable.strings -------------------------------------------------------------------------------- /Battery Time Remaining/zh-Hant-TW.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codler/Battery-Time-Remaining/HEAD/Battery Time Remaining/zh-Hant-TW.lproj/Localizable.strings -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LLStrings.h: -------------------------------------------------------------------------------- 1 | // strings used by LLManager and LaunchAtLoginHelper 2 | // 3 | 4 | #define LLURLScheme @"com.codler.Battery-Time-Remaining2" 5 | #define LLHelperBundleIdentifier @"com.codler.Battery-Time-Remaining-Helper2" 6 | -------------------------------------------------------------------------------- /Battery Time Remaining/Battery Time Remaining-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Battery Time Remaining' target in the 'Battery Time Remaining' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LaunchAtLoginHelper/LLHAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // LLHAppDelegate.h 3 | // LaunchAtLoginHelper 4 | // 5 | // Created by David Keegan on 4/20/12. 6 | // Copyright (c) 2012 David Keegan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LLHAppDelegate : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LaunchAtLoginHelper/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LaunchAtLoginHelper 4 | // 5 | // Created by David Keegan on 4/20/12. 6 | // Copyright (c) 2012 David Keegan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]){ 12 | return NSApplicationMain(argc, (const char **)argv); 13 | } 14 | -------------------------------------------------------------------------------- /Battery Time Remaining/Battery Time Remaining.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LLManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // LLManager.h 3 | // LaunchAtLogin 4 | // 5 | // Created by David Keegan on 4/20/12. 6 | // Copyright (c) 2012 David Keegan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LLManager : NSObject 12 | 13 | + (BOOL)launchAtLogin; 14 | + (void)setLaunchAtLogin:(BOOL)value; 15 | 16 | @property (assign) BOOL launchAtLogin; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Battery Time Remaining/ImageFilter.h: -------------------------------------------------------------------------------- 1 | // 2 | // ImageFilter.h 3 | // Battery Time Remaining 4 | // 5 | // Created by Han Lin Yap on 2012-10-08. 6 | // Copyright (c) 2013 Han Lin Yap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ImageFilter : NSObject 12 | 13 | + (NSImage *)offset:(NSImage *)_image top:(CGFloat)top; 14 | + (NSImage *)invertColor:(NSImage *)_image; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Battery Time Remaining/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf390 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \paperw11900\paperh16840\vieww9600\viewh8400\viewkind0 5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 6 | 7 | \f0\b\fs24 \cf0 Engineering: 8 | \b0 \ 9 | Han Lin Yap\ 10 | \ 11 | App icon provided by happytel.com free calling for all.} -------------------------------------------------------------------------------- /Battery Time Remaining/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Battery Time Remaining 4 | // 5 | // Created by Han Lin Yap on 2012-08-01. 6 | // Copyright (c) 2013 Han Lin Yap. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | AppDelegate * delegate = [[AppDelegate alloc] init]; 15 | [[NSApplication sharedApplication] setDelegate:delegate]; 16 | [NSApp run]; 17 | //return NSApplicationMain(argc, (const char **)argv); 18 | } 19 | -------------------------------------------------------------------------------- /Battery Time Remaining/HttpGet.h: -------------------------------------------------------------------------------- 1 | // 2 | // HttpGet.h 3 | // Battery Time Remaining 4 | // 5 | // Created by Han Lin Yap on 2012-08-12. 6 | // Copyright (c) 2013 Han Lin Yap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HttpGet : NSObject 12 | 13 | + (HttpGet *)sharedHttpGet; 14 | - (void)url:(NSString *)url success:(void(^)(NSString *result))successBlock; 15 | - (void)url:(NSString *)url success:(void(^)(NSString *result))successBlock error:(void(^)(NSError *error))errorBlock; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Han Lin Yap 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/setup.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | import plistlib 3 | 4 | urlScheme = sys.argv[1] 5 | bundleIdentifier = sys.argv[2] 6 | 7 | directory = os.path.dirname(os.path.abspath(__file__)) 8 | 9 | stringsOutput = os.path.join(directory, 'LLStrings.h') 10 | infoPlistOutput = os.path.join(directory, 'LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist') 11 | infoPlist = plistlib.readPlist(os.path.join(directory, 'LaunchAtLoginHelper/LaunchAtLoginHelper-InfoBase.plist')) 12 | 13 | with open(stringsOutput, 'w') as strings: 14 | strings.write("""// strings used by LLManager and LaunchAtLoginHelper 15 | // 16 | 17 | #define LLURLScheme @"%(urlScheme)s" 18 | #define LLHelperBundleIdentifier @"%(bundleIdentifier)s" 19 | """%locals()) 20 | 21 | infoPlist['CFBundleIdentifier'] = bundleIdentifier 22 | plistlib.writePlist(infoPlist, infoPlistOutput) 23 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LaunchAtLoginHelper/LLHAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // LLHAppDelegate.m 3 | // LaunchAtLoginHelper 4 | // 5 | // Created by David Keegan on 4/20/12. 6 | // Copyright (c) 2012 David Keegan. All rights reserved. 7 | // 8 | 9 | #import "LLHAppDelegate.h" 10 | #import "LLStrings.h" 11 | 12 | @implementation LLHAppDelegate 13 | 14 | - (void)applicationDidFinishLaunching:(NSNotification *)notification{ 15 | // Call the scheme to launch the app 16 | NSString *scheme = [NSString stringWithFormat:@"%@://", LLURLScheme]; 17 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:scheme]]; 18 | 19 | // Call the app again this time with `launchedAtLogin` so it knows how it was launched 20 | NSString *schemeLaunchedAtLogin = 21 | [NSString stringWithFormat:@"%@://launchedAtLogin", LLURLScheme]; 22 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:schemeLaunchedAtLogin]]; 23 | [NSApp terminate:self]; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LaunchAtLoginHelper/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Battery Time Remaining/zh-Hant-TW.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg950\cocoartf1187\cocoasubrtf390 2 | {\fonttbl\f0\fnil\fcharset136 STHeitiTC-Light;\f1\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \paperw11900\paperh16840\vieww9600\viewh8400\viewkind0 5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 6 | 7 | \f0\b\fs24 \cf0 \'b5\'7b\'a6\'a1\'b3\'5d\'ad\'70\'a1\'47 8 | \f1\b0 \ 9 | Han Lin Yap\ 10 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 11 | 12 | \b \cf0 \ 13 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 14 | 15 | \f0 \cf0 \'a5\'bf\'c5\'e9\'a4\'a4\'a4\'e5\'c2\'bd\'c4\'b6\'a1\'47 16 | \f1\b0 \ 17 | Kuan Hsueh Lai ({\field{\*\fldinst{HYPERLINK "http://mlkh0225@gmail.com"}}{\fldrslt mlkh0225@gmail.com}})\ 18 | \ 19 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 20 | 21 | \f0 \cf0 \'b5\'7b\'a6\'a1\'b9\'cf\'b9\'b3\'a5\'d1 22 | \f1 happytel.com 23 | \f0 \'a4\'6a\'ae\'61\'a7\'4b\'b6\'4f\'b3\'71\'b8\'dc\'b4\'a3\'a8\'d1\'a1\'43} -------------------------------------------------------------------------------- /Battery Time Remaining/ru.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1251\cocoartf1187\cocoasubrtf340 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \paperw11900\paperh16840\vieww5500\viewh4740\viewkind0 5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 6 | 7 | \f0\b\fs24 \cf0 \uc0\u1056 \u1072 \u1079 \u1088 \u1072 \u1073 \u1086 \u1090 \u1095 \u1080 \u1082 \u1080 : 8 | \b0 \ 9 | Han Lin Yap\ 10 | Mathijs Kadijk \ 11 | \ 12 | 13 | \b \uc0\u1048 \u1082 \u1086 \u1085 \u1082 \u1072 \u1087 \u1088 \u1080 \u1083 \u1086 \u1078 \u1077 \u1085 \u1080 \u1103 : 14 | \b0 \ 15 | {\field{\*\fldinst{HYPERLINK "http://happytel.com/en/"}}{\fldrslt happytel.com}} (\uc0\u1073 \u1077 \u1089 \u1087 \u1083 \u1072 \u1090 \u1085 \u1099 \u1077 \u1079 \u1074 \u1086 \u1085 \u1082 \u1080 )\ 16 | \ 17 | 18 | \b \uc0\u1055 \u1088 \u1086 \u1077 \u1082 \u1090 \u1085 \u1072 GitHub: 19 | \b0 \ 20 | {\field{\*\fldinst{HYPERLINK "https://github.com/codler/Battery-Time-Remaining"}}{\fldrslt Battery-Time-Remaining}}\ 21 | \ 22 | 23 | \i \'a9 2012-2013 \ 24 | {\field{\*\fldinst{HYPERLINK "http://yap.nu/"}}{\fldrslt codler}} & {\field{\*\fldinst{HYPERLINK "http://www.mathijskadijk.nl/"}}{\fldrslt mac-cain13}} 25 | \i0 \ 26 | } -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2012 David Keegan. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LaunchAtLoginHelper/LaunchAtLoginHelper-InfoBase.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.InScopeApps.ShellTo.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2012 David Keegan. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /Battery Time Remaining/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Battery Time Remaining 4 | // 5 | // Created by Han Lin Yap on 2012-08-01. 6 | // Copyright (c) 2013 Han Lin Yap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #ifndef _BTR_MENU 12 | #define _BTR_MENU 13 | 14 | #define kBTRMenuPowerSourcePercent 1 15 | #define kBTRMenuPowerSourceState 2 16 | #define kBTRMenuPowerSourceAdvanced 3 17 | #define kBTRMenuStartAtLogin 4 18 | #define kBTRMenuNotification 5 19 | #define kBTRMenuSetting 6 20 | #define kBTRMenuAdvanced 7 21 | #define kBTRMenuParenthesis 8 22 | #define kBTRMenuFahrenheit 9 23 | #define kBTRMenuPercentage 10 24 | #define kBTRMenuHideIcon 11 25 | #define kBTRMenuHideTime 12 26 | #define kBTRMenuIconRight 13 27 | #define kBTRMenuEnergySaverSetting 14 28 | #define kBTRMenuUpdater 15 29 | #define kBTRMenuQuitKey 16 30 | 31 | #endif 32 | 33 | @interface AppDelegate : NSObject 34 | 35 | - (void)updateStatusItem; 36 | - (NSImage *)getBatteryIconNamed:(NSString *)iconName; 37 | - (NSImage *)getBatteryIconPercent:(NSInteger)percent; 38 | 39 | @property (strong) NSStatusItem *statusItem; 40 | @property (nonatomic) NSInteger previousPercent; 41 | @property (nonatomic) NSInteger currentPercent; 42 | @property (strong) NSMutableDictionary *notifications; 43 | @property (nonatomic) bool advancedSupported; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LLManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // LLManager.m 3 | // LaunchAtLogin 4 | // 5 | // Created by David Keegan on 4/20/12. 6 | // Copyright (c) 2012 David Keegan. All rights reserved. 7 | // 8 | 9 | #import "LLManager.h" 10 | #import "LLStrings.h" 11 | #import 12 | 13 | @implementation LLManager 14 | 15 | + (BOOL)launchAtLogin{ 16 | BOOL launch = NO; 17 | CFArrayRef cfJobs = SMCopyAllJobDictionaries(kSMDomainUserLaunchd); 18 | #if __has_feature(objc_arc) 19 | NSArray *jobs = [NSArray arrayWithArray:(__bridge NSArray *)cfJobs]; 20 | #else 21 | NSArray *jobs = [NSArray arrayWithArray:(NSArray *)cfJobs]; 22 | #endif 23 | CFRelease(cfJobs); 24 | if([jobs count]){ 25 | for(NSDictionary *job in jobs){ 26 | if([job[@"Label"] isEqualToString:LLHelperBundleIdentifier]){ 27 | launch = [job[@"OnDemand"] boolValue]; 28 | break; 29 | } 30 | } 31 | } 32 | return launch; 33 | } 34 | 35 | + (void)setLaunchAtLogin:(BOOL)value{ 36 | if(!SMLoginItemSetEnabled((CFStringRef)LLHelperBundleIdentifier, value)){ 37 | NSLog(@"SMLoginItemSetEnabled failed!"); 38 | } 39 | } 40 | 41 | #pragma mark - Bindings support 42 | 43 | - (BOOL)launchAtLogin { 44 | return [[self class] launchAtLogin]; 45 | } 46 | 47 | - (void)setLaunchAtLogin:(BOOL)launchAtLogin { 48 | [self willChangeValueForKey:@"launchAtLogin"]; 49 | [[self class] setLaunchAtLogin:launchAtLogin]; 50 | [self didChangeValueForKey:@"launchAtLogin"]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Battery Time Remaining/ImageFilter.m: -------------------------------------------------------------------------------- 1 | // 2 | // ImageFilter.m 3 | // Battery Time Remaining 4 | // 5 | // Created by Han Lin Yap on 2012-10-08. 6 | // Copyright (c) 2013 Han Lin Yap. All rights reserved. 7 | // 8 | 9 | #import "ImageFilter.h" 10 | #import 11 | 12 | @implementation ImageFilter 13 | 14 | + (NSImage *)offset:(NSImage *)_image top:(CGFloat)top 15 | { 16 | NSImage *newImage = [[NSImage alloc] initWithSize:NSMakeSize(_image.size.width, _image.size.height + top)]; 17 | [newImage lockFocus]; 18 | 19 | [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; 20 | [_image drawInRect:NSMakeRect(0, 0, _image.size.width, _image.size.height) 21 | fromRect:NSMakeRect(0, 0, _image.size.width, _image.size.height) 22 | operation:NSCompositeSourceOver 23 | fraction:1.0]; 24 | 25 | [newImage unlockFocus]; 26 | 27 | return newImage; 28 | } 29 | 30 | + (NSImage *)invertColor:(NSImage *)_image 31 | { 32 | NSImage *image = [_image copy]; 33 | [image lockFocus]; 34 | 35 | CIImage *ciImage = [[CIImage alloc] initWithData:[image TIFFRepresentation]]; 36 | CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"]; 37 | [filter setDefaults]; 38 | [filter setValue:ciImage forKey:@"inputImage"]; 39 | CIImage *output = [filter valueForKey:@"outputImage"]; 40 | [output drawInRect:NSMakeRect(0, 0, [_image size].width, [_image size].height) fromRect:NSRectFromCGRect([output extent]) operation:NSCompositeSourceOver fraction:1.0]; 41 | 42 | [image unlockFocus]; 43 | 44 | return image; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Battery Time Remaining/Battery Time Remaining-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | icon 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 4.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleTypeRole 27 | Viewer 28 | CFBundleURLName 29 | com.codler.Battery-Time-Remaining2 30 | CFBundleURLSchemes 31 | 32 | com.codler.Battery-Time-Remaining2 33 | 34 | 35 | 36 | CFBundleVersion 37 | 400 38 | LSApplicationCategoryType 39 | public.app-category.utilities 40 | LSHasLocalizedDisplayName 41 | 42 | LSMinimumSystemVersion 43 | ${MACOSX_DEPLOYMENT_TARGET} 44 | LSUIElement 45 | 46 | NSHumanReadableCopyright 47 | Copyright © 2016 Han Lin Yap. All rights reserved. 48 | NSMainNibFile 49 | MainMenu 50 | NSPrincipalClass 51 | NSApplication 52 | NSSupportsAutomaticGraphicsSwitching 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Battery Time Remaining/HttpGet.m: -------------------------------------------------------------------------------- 1 | // 2 | // HttpGet.m 3 | // Battery Time Remaining 4 | // 5 | // Created by Han Lin Yap on 2012-08-12. 6 | // Copyright (c) 2013 Han Lin Yap. All rights reserved. 7 | // 8 | 9 | #import "HttpGet.h" 10 | 11 | static HttpGet *sHttpGet; 12 | 13 | // Private properties 14 | @interface HttpGet() 15 | @property (nonatomic, copy) void(^successBlock)(NSString *result); 16 | @property (nonatomic, copy) void(^errorBlock)(NSError *error); 17 | @property (nonatomic, strong) NSURLConnection *urlConnection; 18 | @property (nonatomic, strong) NSMutableData *tempData; 19 | @end 20 | 21 | @implementation HttpGet 22 | @synthesize successBlock, errorBlock, urlConnection, tempData; 23 | 24 | - (id)init 25 | { 26 | self = [super init]; 27 | if (self) { 28 | self.tempData = [NSMutableData new]; 29 | } 30 | return self; 31 | } 32 | 33 | #pragma mark - Singleton setup 34 | 35 | + (void)initialize 36 | { 37 | NSAssert(self == [HttpGet class], 38 | @"HttpGet is not designed to be subclassed"); 39 | sHttpGet = [HttpGet new]; 40 | } 41 | 42 | + (HttpGet *)sharedHttpGet 43 | { 44 | return sHttpGet; 45 | } 46 | 47 | #pragma mark - Public methods 48 | 49 | - (void)url:(NSString *)url success:(void(^)(NSString *result))aSuccessBlock 50 | { 51 | [self url:url success:aSuccessBlock error:nil]; 52 | } 53 | 54 | - (void)url:(NSString *)url success:(void(^)(NSString *result))aSuccessBlock error:(void(^)(NSError *error))aErrorBlock 55 | { 56 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; 57 | self.successBlock = aSuccessBlock; 58 | self.errorBlock = aErrorBlock; 59 | 60 | // Cancel last url connection if we have one 61 | [self.urlConnection cancel]; 62 | 63 | self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 64 | } 65 | 66 | #pragma mark - NSURLConnectionDelegate methods 67 | 68 | - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 69 | { 70 | [self.tempData setLength:0]; 71 | } 72 | 73 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 74 | { 75 | [self.tempData appendData:data]; 76 | } 77 | 78 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 79 | { 80 | self.errorBlock(error); 81 | 82 | [self.tempData setLength:0]; 83 | self.urlConnection = nil; 84 | } 85 | 86 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection 87 | { 88 | self.successBlock([[NSString alloc] initWithData:self.tempData encoding:NSUTF8StringEncoding]); 89 | 90 | [self.tempData setLength:0]; 91 | self.urlConnection = nil; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /LaunchAtLoginHelper/readme.md: -------------------------------------------------------------------------------- 1 | When creating a sandboxed app `LSSharedFileListInsertItemURL` can no longer be used to launch the app at startup. Instead `SMLoginItemSetEnabled` should be used with a helper app inside the main app's bundle that launches the main app. **LaunchAtLoginHelper** is that helper app, it is designed to be as easy as possible to integrate into the main app to allow it to launch at login when sandboxed. 2 | 3 | A lot of research was put into this helper app. For example [Apple's docs](http://developer.apple.com/library/mac/#documentation/Security/Conceptual/AppSandboxDesignGuide/DesigningYourSandbox/DesigningYourSandbox.html#//apple_ref/doc/uid/TP40011183-CH4-SW3) state that `LSRegisterURL` should be used to register the helper app, however this never seemed to work and after further digging it turns out this is a [typo in the docs](https://devforums.apple.com/message/647212#647212). Many examples I found online used `NSWorkspace launchApplication:` to launch the main app, however this was blocked by sandboxing so a url scheme is used instead. 4 | 5 | **LaunchAtLoginHelper** calls the main app's scheme twice, once to launchg the app and then again with `launchedAtLogin` so the main app can know if it has been launched at login. For example [Play by Play](http://playbyplayapp.com) uses this to hide the app if it was launched at login. 6 | 7 | This project contains a [sample app](https://github.com/kgn/LaunchAtLoginHelper/tree/master/LaunchAtLoginSample) to demonstrate how the main app should be configured and how to setup a checkbox to enable and disable launching at login. 8 | 9 | # How to use 10 | 11 | First download or even better submodule **LaunchAtLoginHelper**. To clone the repository as a submodule use the following commands: 12 | 13 | ``` 14 | $ cd 15 | $ git clone --recursive https://github.com/kgn/LaunchAtLoginHelper.git 16 | ``` 17 | 18 | **LaunchAtLoginHelper** uses a url scheme to launch the main app, if the main app doesn't have a url scheme yet add one. 19 | 20 | ![](http://kgn.github.com/content/launchatlogin/url_scheme.png) 21 | 22 | There are two files missing from this repo that are specific to your instance of the helper app. To generate these files run the `setup.py` python script and pass in the url scheme to launch the main app and the bundle identifier of the helper app, this is usually based of of the bundle identifier of the main app but with *Helper* added onto the end. 23 | 24 | ``` 25 | $ cd LaunchAtLoginHelper 26 | $ python setup.py 27 | ``` 28 | 29 | This will create `LLStrings.h` which is used in both the helper app and the main app and contains `#define`'s for the url scheme and the helper app's bundle identifier. `LaunchAtLoginHelper-Info.plist` is also created for the helper app with it's custom bundle identifier filled in. 30 | 31 | Once these two files are generated it's time to add **LaunchAtLoginHelper** to the main app. Drag `LaunchAtLoginHelper.xcodeproj`, `LLStrings.h`, `LLManager.h`, and `LLManager.m` to the main app's project. 32 | 33 | ![](http://kgn.github.com/content/launchatlogin/drag_drop_file.png) 34 | 35 | Next go to the *Build Phases* for the main app and add **LaunchAtLoginHelper** as a *Target Dependency* and create a new *Copy Files Build Phase*. Set the *Destination* of this build phase to `Wrapper` and the *Subpath* to `Contents/Library/LoginItems`. 36 | 37 | ![](http://kgn.github.com/content/launchatlogin/build_phases.png) 38 | 39 | Lastly add `ServiceManagement.framework` to the main app. 40 | 41 | Once this is done use `LLManager` to enable and disable launching at login! [**LaunchAtLoginSample**](https://github.com/kgn/LaunchAtLoginHelper/blob/master/LaunchAtLoginSample/LLAppDelegate.m) shows how to hook this up to a checkbox. 42 | 43 | ``` obj-c 44 | #import "LLManager.h" 45 | 46 | [LLManager launchAtLogin] // will the app launch at login? 47 | [LLManager setLaunchAtLogin:YES] // set the app to launch at login 48 | ``` 49 | 50 | # Bindings 51 | 52 | The `LLManager` class supports KVO and Cocoa Bindings. This allows for a completely code-free implementaion of this class. To get started, open the Interface Builder document in which you plan to create a login toggle. Drag a generic `NSObject` from the Utilities pane, and drop it onto your canvas. Select the newly created object, and open the Identity inspector tab in the Utilities pane. Change the class from `NSObject` to `LLManager`. Now, select to your login toggle (checkbox) and open the Bindings inspector in the Utilities pane. Expand `Value`, check the "Bind to", and select the name of the `LLManager` object you created earlier. Set the key path to `self.launchAtLogin`. You're done! 53 | 54 | --- 55 | 56 | Special thanks to [Curtis Hard](http://www.geekygoodness.com) for offering some much needed advice on this project. -------------------------------------------------------------------------------- /LaunchAtLoginHelper/LaunchAtLoginHelper.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 734C379915414CE200994189 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 734C379815414CE200994189 /* Cocoa.framework */; }; 11 | 73C920E915414E7800C2A75A /* LLHAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C920E615414E7800C2A75A /* LLHAppDelegate.m */; }; 12 | 73C920EA15414E7800C2A75A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C920E715414E7800C2A75A /* main.m */; }; 13 | 73C920EB15414E7800C2A75A /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 73C920E815414E7800C2A75A /* MainMenu.xib */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | 734C379415414CE200994189 /* LaunchAtLoginHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LaunchAtLoginHelper.app; sourceTree = BUILT_PRODUCTS_DIR; }; 18 | 734C379815414CE200994189 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 19 | 734C379B15414CE200994189 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 20 | 734C379D15414CE200994189 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 21 | 73C920E515414E7800C2A75A /* LLHAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLHAppDelegate.h; path = LaunchAtLoginHelper/LLHAppDelegate.h; sourceTree = ""; }; 22 | 73C920E615414E7800C2A75A /* LLHAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LLHAppDelegate.m; path = LaunchAtLoginHelper/LLHAppDelegate.m; sourceTree = ""; }; 23 | 73C920E715414E7800C2A75A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = LaunchAtLoginHelper/main.m; sourceTree = ""; }; 24 | 73C920E815414E7800C2A75A /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = MainMenu.xib; path = LaunchAtLoginHelper/MainMenu.xib; sourceTree = ""; }; 25 | 73C920FA15414F7000C2A75A /* LLStrings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LLStrings.h; sourceTree = SOURCE_ROOT; }; 26 | 73C920FB15414FC900C2A75A /* LaunchAtLoginHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = LaunchAtLoginHelper.entitlements; path = LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 734C379115414CE200994189 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | 734C379915414CE200994189 /* Cocoa.framework in Frameworks */, 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | 734C378915414CE200994189 = { 42 | isa = PBXGroup; 43 | children = ( 44 | 73C920E715414E7800C2A75A /* main.m */, 45 | 73C920FA15414F7000C2A75A /* LLStrings.h */, 46 | 73C920E515414E7800C2A75A /* LLHAppDelegate.h */, 47 | 73C920E615414E7800C2A75A /* LLHAppDelegate.m */, 48 | 73C920E815414E7800C2A75A /* MainMenu.xib */, 49 | 73C920FB15414FC900C2A75A /* LaunchAtLoginHelper.entitlements */, 50 | 734C379715414CE200994189 /* Frameworks */, 51 | 734C379515414CE200994189 /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 734C379515414CE200994189 /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 734C379415414CE200994189 /* LaunchAtLoginHelper.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 734C379715414CE200994189 /* Frameworks */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 734C379B15414CE200994189 /* AppKit.framework */, 67 | 734C379D15414CE200994189 /* Foundation.framework */, 68 | 734C379815414CE200994189 /* Cocoa.framework */, 69 | ); 70 | name = Frameworks; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | 734C379315414CE200994189 /* LaunchAtLoginHelper */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = 734C37B215414CE200994189 /* Build configuration list for PBXNativeTarget "LaunchAtLoginHelper" */; 79 | buildPhases = ( 80 | 734C379015414CE200994189 /* Sources */, 81 | 734C379115414CE200994189 /* Frameworks */, 82 | 734C379215414CE200994189 /* Resources */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = LaunchAtLoginHelper; 89 | productName = LaunchAtLoginHelper; 90 | productReference = 734C379415414CE200994189 /* LaunchAtLoginHelper.app */; 91 | productType = "com.apple.product-type.application"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | 734C378B15414CE200994189 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | CLASSPREFIX = LLH; 100 | LastUpgradeCheck = 0800; 101 | ORGANIZATIONNAME = "David Keegan"; 102 | TargetAttributes = { 103 | 734C379315414CE200994189 = { 104 | DevelopmentTeam = 9K37AQ8P25; 105 | }; 106 | }; 107 | }; 108 | buildConfigurationList = 734C378E15414CE200994189 /* Build configuration list for PBXProject "LaunchAtLoginHelper" */; 109 | compatibilityVersion = "Xcode 3.2"; 110 | developmentRegion = English; 111 | hasScannedForEncodings = 0; 112 | knownRegions = ( 113 | en, 114 | ); 115 | mainGroup = 734C378915414CE200994189; 116 | productRefGroup = 734C379515414CE200994189 /* Products */; 117 | projectDirPath = ""; 118 | projectRoot = ""; 119 | targets = ( 120 | 734C379315414CE200994189 /* LaunchAtLoginHelper */, 121 | ); 122 | }; 123 | /* End PBXProject section */ 124 | 125 | /* Begin PBXResourcesBuildPhase section */ 126 | 734C379215414CE200994189 /* Resources */ = { 127 | isa = PBXResourcesBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | 73C920EB15414E7800C2A75A /* MainMenu.xib in Resources */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | /* End PBXResourcesBuildPhase section */ 135 | 136 | /* Begin PBXSourcesBuildPhase section */ 137 | 734C379015414CE200994189 /* Sources */ = { 138 | isa = PBXSourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | 73C920E915414E7800C2A75A /* LLHAppDelegate.m in Sources */, 142 | 73C920EA15414E7800C2A75A /* main.m in Sources */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | /* End PBXSourcesBuildPhase section */ 147 | 148 | /* Begin XCBuildConfiguration section */ 149 | 734C37B015414CE200994189 /* Debug */ = { 150 | isa = XCBuildConfiguration; 151 | buildSettings = { 152 | ALWAYS_SEARCH_USER_PATHS = NO; 153 | CLANG_WARN_BOOL_CONVERSION = YES; 154 | CLANG_WARN_CONSTANT_CONVERSION = YES; 155 | CLANG_WARN_EMPTY_BODY = YES; 156 | CLANG_WARN_ENUM_CONVERSION = YES; 157 | CLANG_WARN_INFINITE_RECURSION = YES; 158 | CLANG_WARN_INT_CONVERSION = YES; 159 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 160 | CLANG_WARN_UNREACHABLE_CODE = YES; 161 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 162 | COMBINE_HIDPI_IMAGES = YES; 163 | COPY_PHASE_STRIP = NO; 164 | ENABLE_STRICT_OBJC_MSGSEND = YES; 165 | ENABLE_TESTABILITY = YES; 166 | GCC_C_LANGUAGE_STANDARD = gnu99; 167 | GCC_DYNAMIC_NO_PIC = NO; 168 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 169 | GCC_NO_COMMON_BLOCKS = YES; 170 | GCC_OPTIMIZATION_LEVEL = 0; 171 | GCC_PREPROCESSOR_DEFINITIONS = ( 172 | "DEBUG=1", 173 | "$(inherited)", 174 | ); 175 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 176 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 177 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 178 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 179 | GCC_WARN_UNDECLARED_SELECTOR = YES; 180 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 181 | GCC_WARN_UNUSED_FUNCTION = YES; 182 | GCC_WARN_UNUSED_VARIABLE = YES; 183 | MACOSX_DEPLOYMENT_TARGET = 10.6; 184 | ONLY_ACTIVE_ARCH = YES; 185 | SDKROOT = macosx; 186 | SKIP_INSTALL = YES; 187 | }; 188 | name = Debug; 189 | }; 190 | 734C37B115414CE200994189 /* Release */ = { 191 | isa = XCBuildConfiguration; 192 | buildSettings = { 193 | ALWAYS_SEARCH_USER_PATHS = NO; 194 | CLANG_WARN_BOOL_CONVERSION = YES; 195 | CLANG_WARN_CONSTANT_CONVERSION = YES; 196 | CLANG_WARN_EMPTY_BODY = YES; 197 | CLANG_WARN_ENUM_CONVERSION = YES; 198 | CLANG_WARN_INFINITE_RECURSION = YES; 199 | CLANG_WARN_INT_CONVERSION = YES; 200 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 201 | CLANG_WARN_UNREACHABLE_CODE = YES; 202 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 203 | COMBINE_HIDPI_IMAGES = YES; 204 | COPY_PHASE_STRIP = YES; 205 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 206 | ENABLE_STRICT_OBJC_MSGSEND = YES; 207 | GCC_C_LANGUAGE_STANDARD = gnu99; 208 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 209 | GCC_NO_COMMON_BLOCKS = YES; 210 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 211 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 212 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 213 | GCC_WARN_UNDECLARED_SELECTOR = YES; 214 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 215 | GCC_WARN_UNUSED_FUNCTION = YES; 216 | GCC_WARN_UNUSED_VARIABLE = YES; 217 | MACOSX_DEPLOYMENT_TARGET = 10.6; 218 | SDKROOT = macosx; 219 | SKIP_INSTALL = YES; 220 | }; 221 | name = Release; 222 | }; 223 | 734C37B315414CE200994189 /* Debug */ = { 224 | isa = XCBuildConfiguration; 225 | buildSettings = { 226 | CODE_SIGN_ENTITLEMENTS = LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements; 227 | CODE_SIGN_IDENTITY = "Mac Developer"; 228 | DEVELOPMENT_TEAM = 9K37AQ8P25; 229 | INFOPLIST_FILE = "LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist"; 230 | MACOSX_DEPLOYMENT_TARGET = 10.6.8; 231 | PRODUCT_BUNDLE_IDENTIFIER = "com.codler.Battery-Time-Remaining-Helper2"; 232 | PRODUCT_NAME = "$(TARGET_NAME)"; 233 | PROVISIONING_PROFILE = ""; 234 | WRAPPER_EXTENSION = app; 235 | }; 236 | name = Debug; 237 | }; 238 | 734C37B415414CE200994189 /* Release */ = { 239 | isa = XCBuildConfiguration; 240 | buildSettings = { 241 | CODE_SIGN_ENTITLEMENTS = LaunchAtLoginHelper/LaunchAtLoginHelper.entitlements; 242 | CODE_SIGN_IDENTITY = "Mac Developer"; 243 | DEVELOPMENT_TEAM = 9K37AQ8P25; 244 | INFOPLIST_FILE = "LaunchAtLoginHelper/LaunchAtLoginHelper-Info.plist"; 245 | MACOSX_DEPLOYMENT_TARGET = 10.6.8; 246 | PRODUCT_BUNDLE_IDENTIFIER = "com.codler.Battery-Time-Remaining-Helper2"; 247 | PRODUCT_NAME = "$(TARGET_NAME)"; 248 | PROVISIONING_PROFILE = ""; 249 | WRAPPER_EXTENSION = app; 250 | }; 251 | name = Release; 252 | }; 253 | /* End XCBuildConfiguration section */ 254 | 255 | /* Begin XCConfigurationList section */ 256 | 734C378E15414CE200994189 /* Build configuration list for PBXProject "LaunchAtLoginHelper" */ = { 257 | isa = XCConfigurationList; 258 | buildConfigurations = ( 259 | 734C37B015414CE200994189 /* Debug */, 260 | 734C37B115414CE200994189 /* Release */, 261 | ); 262 | defaultConfigurationIsVisible = 0; 263 | defaultConfigurationName = Release; 264 | }; 265 | 734C37B215414CE200994189 /* Build configuration list for PBXNativeTarget "LaunchAtLoginHelper" */ = { 266 | isa = XCConfigurationList; 267 | buildConfigurations = ( 268 | 734C37B315414CE200994189 /* Debug */, 269 | 734C37B415414CE200994189 /* Release */, 270 | ); 271 | defaultConfigurationIsVisible = 0; 272 | defaultConfigurationName = Release; 273 | }; 274 | /* End XCConfigurationList section */ 275 | }; 276 | rootObject = 734C378B15414CE200994189 /* Project object */; 277 | } 278 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Battery Time Remaining 2 2 | ====================== 3 | 4 | Show the estimated battery time remaining on top of your screen in Mac OS X 10.8 Mountain Lion. 5 | 6 | ![Normal mode](https://raw.github.com/codler/Battery-Time-Remaining/master/preview.png) 7 | ![Advanced mode](https://raw.github.com/codler/Battery-Time-Remaining/master/preview_advanced.png) 8 | 9 | Why does this project exist? 10 | ----------------------------- 11 | 12 | Apple removed the option to show the battery time remaining in the statusbar since the Mountain Lion release. This App will do exactly that, show the battery time remaining on top of your screen. 13 | 14 | How do I install it? 15 | -------------------- 16 | 17 | Three options: 18 | 19 | - Download from [App Store](https://itunes.apple.com/us/app/battery-time-2/id665678267?mt=12) 20 | - Download [latest version](http://yap.nu/battery-time-remaining/), unzip and run the App 21 | - Download the source here from Github and compile it with XCode 22 | 23 | Is it accurate? 24 | --------------- 25 | 26 | The App shows the exact same time as you will see when you click the battery icon. The time is provided by Mac OS X itself and as accurate as you can get. 27 | 28 | How do I contribute? 29 | -------------------- 30 | 31 | Fork this project, make some changes and submit a pull request. Check the issues tab for inspiration on what to fix. Please make sure your fork is the latest development version! 32 | 33 | If you find any issues or have a feature request please contribute by submitting an issue here on Github! 34 | 35 | If you would like to donate some bitcoins: 36 | 37 | 19H9hk9LgALN41msp67LYysLLyYN23q7Y4 38 | 39 | Who did make this app? 40 | ---------------------- 41 | 42 | * [Han Lin Yap](https://github.com/codler) and thanks to [contributors](https://github.com/codler/Battery-Time-Remaining/graphs/contributors)! 43 | 44 | App icon provided by happytel.com free calling for all. 45 | 46 | Change log 47 | ---------- 48 | 49 | 2016-10-15 - **v4.0** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v3.1...v4.0) 50 | 51 | * Added support for macOS Sierra 10.12 ([nbppp2](https://github.com/nbppp2) [#105](https://github.com/codler/Battery-Time-Remaining/issues/105) [#107](https://github.com/codler/Battery-Time-Remaining/pull/107)) 52 | 53 | 2016-10-15 - **v1.8** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.7.1..v1.8) 54 | 55 | * Added support for macOS Sierra 10.12 ([codler](https://github.com/codler) [#105](https://github.com/codler/Battery-Time-Remaining/issues/105) [#107](https://github.com/codler/Battery-Time-Remaining/pull/107)) 56 | 57 | 2014-11-16 - **v3.1** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v3.0.1...v3.1) 58 | 59 | * Added Icon placement setting ([codler](https://github.com/codler) [#72](https://github.com/codler/Battery-Time-Remaining/issues/72)) 60 | 61 | 2014-10-28 - **v3.0.1** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v3.0...v3.0.1) 62 | 63 | * Fix dark mode ([codler](https://github.com/codler)) 64 | 65 | 2014-10-28 - **v1.7.1** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.7...v1.7.1) 66 | 67 | * Fix dark mode ([codler](https://github.com/codler)) 68 | 69 | 2014-10-18 - **v1.7** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.6.5...v1.7) 70 | 71 | * Added support for OS X 10.10 Yosemite ([codler](https://github.com/codler) [#89](https://github.com/codler/Battery-Time-Remaining/issues/89)) 72 | 73 | 2014-10-17 - **v3.0** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v2.1...v3.0) 74 | 75 | * Added support for OS X 10.10 Yosemite ([codler](https://github.com/codler) [#89](https://github.com/codler/Battery-Time-Remaining/issues/89)) 76 | 77 | 2014-01-02 - **v2.1** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v2.0.2...v2.1) 78 | 79 | * Added notifications while charging ([codler](https://github.com/codler) [#79](https://github.com/codler/Battery-Time-Remaining/issues/79)) 80 | * Updated German language ([Velines](https://github.com/Velines) [#80](https://github.com/codler/Battery-Time-Remaining/pull/80)) 81 | 82 | 2013-10-23 - **v2.0.2** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v2.0.1...v2.0.2) 83 | 84 | * Updated French language ([Vinky41](https://github.com/Vinky41) [#69](https://github.com/codler/Battery-Time-Remaining/pull/69)) 85 | * Updated Traditional Chinese Taiwan language ([mlkh0225](https://github.com/mlkh0225) [#73](https://github.com/codler/Battery-Time-Remaining/pull/73)) 86 | * Fix allow Mac with 2 GPUs to utilize integrated GPU ([airdrummingfool](https://github.com/airdrummingfool) [#74](https://github.com/codler/Battery-Time-Remaining/issues/74) [#75](https://github.com/codler/Battery-Time-Remaining/pull/75)) 87 | 88 | 2013-10-23 - **v1.6.5** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.6.4...v1.6.5) 89 | 90 | * Update French language ([Vinky41](https://github.com/Vinky41) [#69](https://github.com/codler/Battery-Time-Remaining/pull/69)) 91 | * Fix allow Mac with 2 GPUs to utilize integrated GPU ([airdrummingfool](https://github.com/airdrummingfool) [#74](https://github.com/codler/Battery-Time-Remaining/issues/74) [#75](https://github.com/codler/Battery-Time-Remaining/pull/75)) 92 | 93 | 2013-09-03 - **v2.0.1** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v2.0...v2.0.1) 94 | 95 | * Added Hide time setting ([codler](https://github.com/codler)) 96 | * Added Traditional Chinese Taiwan language ([mlkh0225](https://github.com/mlkh0225) [#68](https://github.com/codler/Battery-Time-Remaining/pull/68)) 97 | * Improved status bar, able to see both percentage and time as same time. ([codler](https://github.com/codler) [#65](https://github.com/codler/Battery-Time-Remaining/issues/65)) 98 | * Fix bug not showing correct color on menu select ([codler](https://github.com/codler)) 99 | * Fix bug not showing parenthesis when showing percentage ([codler](https://github.com/codler)) 100 | 101 | 2013-09-03 - **v1.6.4** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.6.3...v1.6.4) 102 | 103 | * Added Traditional Chinese Taiwan language ([mlkh0225](https://github.com/mlkh0225) [#68](https://github.com/codler/Battery-Time-Remaining/pull/68)) 104 | 105 | 2013-07-05 - **v2.0** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.6.3...v2.0) 106 | 107 | * Added Hide icon setting ([codler](https://github.com/codler) [#51](https://github.com/codler/Battery-Time-Remaining/issues/51)) 108 | * Added Display Fahrenheit setting ([codler](https://github.com/codler) [danielmartin](https://github.com/danielmartin) [#60](https://github.com/codler/Battery-Time-Remaining/issues/60) [#62](https://github.com/codler/Battery-Time-Remaining/pull/62)) 109 | * Added Display percentage setting ([codler](https://github.com/codler) [#55](https://github.com/codler/Battery-Time-Remaining/issues/55)) 110 | * Added Display white text setting ([codler](https://github.com/codler) [JohnnySlagle](https://github.com/JohnnySlagle) [#64](https://github.com/codler/Battery-Time-Remaining/pull/64)) 111 | * Show time since unplugged (advanced mode) ([alexanderad](https://github.com/alexanderad) [codler](https://github.com/codler) [#54](https://github.com/codler/Battery-Time-Remaining/pull/54)) 112 | 113 | 2013-06-30 - **v1.6.3** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.6.2...v1.6.3) 114 | 115 | * Added Russian language ([ericbroska](https://github.com/ericbroska) [#58](https://github.com/codler/Battery-Time-Remaining/pull/58)) 116 | 117 | 2013-01-20 - **v1.6.2** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.6.1...v1.6.2) 118 | 119 | * Added Spanish language ([danielmartin](https://github.com/danielmartin) [#56](https://github.com/codler/Battery-Time-Remaining/pull/56)) 120 | * Change home url and update url ([codler](https://github.com/codler) [#57](https://github.com/codler/Battery-Time-Remaining/issues/57)) 121 | 122 | 2012-11-01 - **v1.6.1** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.6...v1.6.1) 123 | 124 | * Added Italian language ([DMG1](https://github.com/DMG1) [#46](https://github.com/codler/Battery-Time-Remaining/issues/46)) 125 | * Added Korean language ([JustyStyle](https://github.com/justystyle)) 126 | * Updated French, German, Swedish languages ([codler](https://github.com/codler) [Velines](https://github.com/Velines) [Vinky41](https://github.com/Vinky41) [#48](https://github.com/codler/Battery-Time-Remaining/pull/48)) 127 | * Prompt auto update ([codler](https://github.com/codler)) 128 | 129 | 2012-10-03 - **v1.6** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.5.2...v1.6) 130 | 131 | * New app icon ([dizel247](https://github.com/dizel247) [#11](https://github.com/codler/Battery-Time-Remaining/issues/11)) 132 | * Added fast switch between advanced mode by pressing option key. ([codler](https://github.com/codler)) 133 | * Added Polish language ([alamilar](https://github.com/alamilar) [#38](https://github.com/codler/Battery-Time-Remaining/issues/38)) 134 | * Added Portuguese language (dvm) 135 | * Added Simplified Chinese language ([zhangwen590](https://github.com/zhangwen590)) 136 | * Improved notification ([codler](https://github.com/codler) [#20](https://github.com/codler/Battery-Time-Remaining/issues/20)) 137 | * Improved translation ([codler](https://github.com/codler) [#26](https://github.com/codler/Battery-Time-Remaining/issues/26)) 138 | * Improved battery icon ([codler](https://github.com/codler) [#29](https://github.com/codler/Battery-Time-Remaining/issues/29)) 139 | * Improved menu ([codler](https://github.com/codler)) 140 | * Fix memory leaks ([codler](https://github.com/codler)) 141 | * Fix bug not showing correct on logon ([codler](https://github.com/codler) [#22](https://github.com/codler/Battery-Time-Remaining/issues/22)) 142 | 143 | 2012-08-29 - **v1.5.2** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.5.1...v1.5.2) 144 | 145 | * Added Display time with parantheses setting ([c-alpha](https://github.com/c-alpha) [#19](https://github.com/codler/Battery-Time-Remaining/pull/19)) 146 | * Added Slovak language ([miroslavchutnak](https://github.com/miroslavchutnak) [#34](https://github.com/codler/Battery-Time-Remaining/issues/34)) 147 | * Updated existing languages ([codler](https://github.com/codler) [c-alpha](https://github.com/c-alpha) [guillaume-algis](https://github.com/guillaume-algis) [mac-cain13](https://github.com/mac-cain13) [Velines](https://github.com/Velines) [Vinky41](https://github.com/Vinky41) [#21](https://github.com/codler/Battery-Time-Remaining/pull/21) [#25](https://github.com/codler/Battery-Time-Remaining/pull/25) [#27](https://github.com/codler/Battery-Time-Remaining/pull/27)) 148 | * Improved battery icon ([codler](https://github.com/codler) [c-alpha](https://github.com/c-alpha) [#19](https://github.com/codler/Battery-Time-Remaining/pull/19)) 149 | * Fix bug not showing red color in battery icon in v1.5.1 150 | 151 | 2012-08-25 - **v1.5.1** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.5...v1.5.1) 152 | 153 | * Added French language ([guillaume-algis](https://github.com/guillaume-algis) [#16](https://github.com/codler/Battery-Time-Remaining/pull/16)) 154 | * Added German language ([Velines](https://github.com/Velines) [#18](https://github.com/codler/Battery-Time-Remaining/pull/18)) 155 | * Improved battery icon by adding white drop shadow ([guillaume-algis](https://github.com/guillaume-algis) [#16](https://github.com/codler/Battery-Time-Remaining/pull/16) [#5](https://github.com/codler/Battery-Time-Remaining/issues/5)) 156 | * Fix a bug on battery icon in retina display ([codler](https://github.com/codler) [#13](https://github.com/codler/Battery-Time-Remaining/issues/13)) 157 | * Fix a bug not showing batteryCharged icon ([codler](https://github.com/codler)) 158 | 159 | 2012-08-19 - **v1.5** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.4...v1.5) 160 | 161 | * Added language support ([mac-cain13](https://github.com/mac-cain13) [#9](https://github.com/codler/Battery-Time-Remaining/pull/9)) 162 | * Added Dutch language ([mac-cain13](https://github.com/mac-cain13) [#9](https://github.com/codler/Battery-Time-Remaining/pull/9) [#10](https://github.com/codler/Battery-Time-Remaining/pull/10)) 163 | * Added Swedish language ([codler](https://github.com/codler)) 164 | * Added advanced mode section ([codler](https://github.com/codler)) 165 | * Show battery temperature (advanced mode) ([codler](https://github.com/codler)) 166 | * Show power usage (advanced mode) ([codler](https://github.com/codler)) 167 | * Show battery cycle count (advanced mode) ([codler](https://github.com/codler)) 168 | * Show battery in mAh (advanced mode) ([codler](https://github.com/codler)) 169 | * Improved battery icon ([codler](https://github.com/codler) [#8](https://github.com/codler/Battery-Time-Remaining/issues/8)) 170 | * Improved app icon ([codler](https://github.com/codler) [#11](https://github.com/codler/Battery-Time-Remaining/issues/11)) 171 | * Code improvements ([codler](https://github.com/codler)) 172 | * Smaller text in menubar ([codler](https://github.com/codler) [#12](https://github.com/codler/Battery-Time-Remaining/issues/12)) 173 | 174 | 2012-08-15 - **v1.4** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.3...v1.4) 175 | 176 | * App sandbox ([codler](https://github.com/codler)) 177 | * Rewrote start at login ([codler](https://github.com/codler) [#4](https://github.com/codler/Battery-Time-Remaining/issues/4)) 178 | * Improved battery icon ([codler](https://github.com/codler)) 179 | * Improved notification ([codler](https://github.com/codler)) 180 | 181 | 2012-08-12 - **v1.3** - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.2.1...v1.3) 182 | 183 | * Added check for updates ([codler](https://github.com/codler)) 184 | * Display battery percentage left in menu ([codler](https://github.com/codler) [#6](https://github.com/codler/Battery-Time-Remaining/issues/6)) 185 | 186 | 2012-08-09 - **v1.2.1** *(signed app)* - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.2...v1.2.1) 187 | 188 | * Added notifications ([codler](https://github.com/codler)) 189 | * Added open energy saver preferences option ([codler](https://github.com/codler)) 190 | 191 | 2012-08-08 - **v1.2** *(signed app)* - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.1.1...v1.2) 192 | 193 | * From now on the app will always be signed. ([codler](https://github.com/codler) [#3](https://github.com/codler/Battery-Time-Remaining/issues/3)) 194 | * Added higher resolution app icon ([codler](https://github.com/codler)) 195 | 196 | 2012-08-06 - **v1.1.1** *(unsigned app)* - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.1...v1.1.1) 197 | 198 | * Added app icon ([mac-cain13](https://github.com/mac-cain13) [#2](https://github.com/codler/Battery-Time-Remaining/pull/2)) 199 | * Improved battery icon ([mac-cain13](https://github.com/mac-cain13) [#2](https://github.com/codler/Battery-Time-Remaining/pull/2)) 200 | * Removed MainMenu.xib ([codler](https://github.com/codler)) 201 | 202 | 2012-08-05 - **v1.1** *(unsigned app)* - [diff](https://github.com/codler/Battery-Time-Remaining/compare/v1.0...v1.1) 203 | 204 | * Added battery icon ([mac-cain13](https://github.com/mac-cain13) [#1](https://github.com/codler/Battery-Time-Remaining/pull/1)) 205 | * Added start at login option ([mac-cain13](https://github.com/mac-cain13) [#1](https://github.com/codler/Battery-Time-Remaining/pull/1)) 206 | * Added readme ([mac-cain13](https://github.com/mac-cain13) [#1](https://github.com/codler/Battery-Time-Remaining/pull/1)) 207 | * Improved battery icon ([codler](https://github.com/codler)) 208 | * Improved time remaining text ([mac-cain13](https://github.com/mac-cain13) [#1](https://github.com/codler/Battery-Time-Remaining/pull/1)) 209 | 210 | 2012-08-01 - **v1.0** *(unsigned app)* 211 | 212 | * First commit. ([codler](https://github.com/codler)) 213 | -------------------------------------------------------------------------------- /Battery Time Remaining.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 280E9EBD15E0D18C00530934 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 280E9EBC15E0D18C00530934 /* QuartzCore.framework */; }; 11 | 2824942D17AE658B00DD0F74 /* build_version2 in Resources */ = {isa = PBXBuildFile; fileRef = 2824942C17AE658B00DD0F74 /* build_version2 */; }; 12 | 2841C7E115C91CC100F4F15F /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2841C7E015C91CC100F4F15F /* Cocoa.framework */; }; 13 | 2841C7ED15C91CC100F4F15F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2841C7EC15C91CC100F4F15F /* main.m */; }; 14 | 2841C7F115C91CC100F4F15F /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2841C7EF15C91CC100F4F15F /* Credits.rtf */; }; 15 | 2841C7F415C91CC100F4F15F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2841C7F315C91CC100F4F15F /* AppDelegate.m */; }; 16 | 2841C7FE15C91CEF00F4F15F /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2841C7FD15C91CEF00F4F15F /* IOKit.framework */; }; 17 | 28473E3C1787505700D6CC7C /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 28473E3B1787505700D6CC7C /* LICENSE */; }; 18 | 28527E581622E8A800EE3644 /* ImageFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 28527E571622E8A800EE3644 /* ImageFilter.m */; }; 19 | 28833BAE15D7B90B00741727 /* HttpGet.m in Sources */ = {isa = PBXBuildFile; fileRef = 28833BAD15D7B90B00741727 /* HttpGet.m */; }; 20 | 28833BB215D7D4FF00741727 /* Readme.md in Resources */ = {isa = PBXBuildFile; fileRef = 28833BB115D7D4FF00741727 /* Readme.md */; }; 21 | 28833BB415D7D61100741727 /* build_version in Resources */ = {isa = PBXBuildFile; fileRef = 28833BB315D7D61100741727 /* build_version */; }; 22 | 28BA61A815D1B12500EDB674 /* icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 28BA61A715D1B12500EDB674 /* icon.icns */; }; 23 | 28FEDA5E15DA5C9D00320B72 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FEDA5D15DA5C9D00320B72 /* ServiceManagement.framework */; }; 24 | 28FEDA6E15DA5ECA00320B72 /* LaunchAtLoginHelper.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 28FEDA6B15DA5EB200320B72 /* LaunchAtLoginHelper.app */; }; 25 | 28FEDA6F15DA5F8C00320B72 /* LLManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 28FEDA6315DA5EB200320B72 /* LLManager.m */; }; 26 | D53375E515DC43380077CEF6 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D53375E315DC43380077CEF6 /* Localizable.strings */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 28FEDA6A15DA5EB200320B72 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 28FEDA5F15DA5EB200320B72 /* LaunchAtLoginHelper.xcodeproj */; 33 | proxyType = 2; 34 | remoteGlobalIDString = 734C379415414CE200994189; 35 | remoteInfo = LaunchAtLoginHelper; 36 | }; 37 | 28FEDA6C15DA5EC200320B72 /* PBXContainerItemProxy */ = { 38 | isa = PBXContainerItemProxy; 39 | containerPortal = 28FEDA5F15DA5EB200320B72 /* LaunchAtLoginHelper.xcodeproj */; 40 | proxyType = 1; 41 | remoteGlobalIDString = 734C379315414CE200994189; 42 | remoteInfo = LaunchAtLoginHelper; 43 | }; 44 | /* End PBXContainerItemProxy section */ 45 | 46 | /* Begin PBXCopyFilesBuildPhase section */ 47 | 28FEDA5B15DA5B5900320B72 /* CopyFiles */ = { 48 | isa = PBXCopyFilesBuildPhase; 49 | buildActionMask = 2147483647; 50 | dstPath = Contents/Library/LoginItems; 51 | dstSubfolderSpec = 1; 52 | files = ( 53 | 28FEDA6E15DA5ECA00320B72 /* LaunchAtLoginHelper.app in CopyFiles */, 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXCopyFilesBuildPhase section */ 58 | 59 | /* Begin PBXFileReference section */ 60 | 280E9EBC15E0D18C00530934 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 61 | 2822133616369BB70019A3EE /* ko */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; 62 | 2824942C17AE658B00DD0F74 /* build_version2 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = build_version2; sourceTree = SOURCE_ROOT; }; 63 | 2824FFEF1611A5D500CD3140 /* zh-Hans */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 64 | 28369FFA15EE902E005957A0 /* sk */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = ""; }; 65 | 2841C7DC15C91CC100F4F15F /* Battery Time Remaining 2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Battery Time Remaining 2.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 2841C7E015C91CC100F4F15F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 67 | 2841C7E315C91CC100F4F15F /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 68 | 2841C7E415C91CC100F4F15F /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 69 | 2841C7E515C91CC100F4F15F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 70 | 2841C7E815C91CC100F4F15F /* Battery Time Remaining-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Battery Time Remaining-Info.plist"; sourceTree = ""; }; 71 | 2841C7EC15C91CC100F4F15F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 72 | 2841C7EE15C91CC100F4F15F /* Battery Time Remaining-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Battery Time Remaining-Prefix.pch"; sourceTree = ""; }; 73 | 2841C7F015C91CC100F4F15F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 74 | 2841C7F215C91CC100F4F15F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 75 | 2841C7F315C91CC100F4F15F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 76 | 2841C7FD15C91CEF00F4F15F /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 77 | 28473E3B1787505700D6CC7C /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; }; 78 | 28527E561622E8A800EE3644 /* ImageFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFilter.h; sourceTree = ""; }; 79 | 28527E571622E8A800EE3644 /* ImageFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageFilter.m; sourceTree = ""; }; 80 | 28563BBE1621A34500960191 /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; 81 | 288305DF15DD005C0064D457 /* sv */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; 82 | 28833BAC15D7B90B00741727 /* HttpGet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HttpGet.h; sourceTree = ""; }; 83 | 28833BAD15D7B90B00741727 /* HttpGet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HttpGet.m; sourceTree = ""; }; 84 | 28833BB115D7D4FF00741727 /* Readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Readme.md; sourceTree = SOURCE_ROOT; }; 85 | 28833BB315D7D61100741727 /* build_version */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = build_version; sourceTree = SOURCE_ROOT; }; 86 | 28BA61A715D1B12500EDB674 /* icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = icon.icns; path = ../icon.icns; sourceTree = ""; }; 87 | 28BA61A915D1B63300EDB674 /* Battery Time Remaining.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Battery Time Remaining.entitlements"; sourceTree = ""; }; 88 | 28C8E25415F684CD009AA6DB /* pt */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = ""; }; 89 | 28FEDA5D15DA5C9D00320B72 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; }; 90 | 28FEDA5F15DA5EB200320B72 /* LaunchAtLoginHelper.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LaunchAtLoginHelper.xcodeproj; path = LaunchAtLoginHelper/LaunchAtLoginHelper.xcodeproj; sourceTree = ""; }; 91 | 28FEDA6215DA5EB200320B72 /* LLManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LLManager.h; path = ../LaunchAtLoginHelper/LLManager.h; sourceTree = ""; }; 92 | 28FEDA6315DA5EB200320B72 /* LLManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = LLManager.m; path = ../LaunchAtLoginHelper/LLManager.m; sourceTree = ""; }; 93 | 28FEDA6415DA5EB200320B72 /* LLStrings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = LLStrings.h; path = ../LaunchAtLoginHelper/LLStrings.h; sourceTree = ""; }; 94 | 2CDF24CA16B51EC500B8B8C0 /* ru */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; 95 | 2CDF24CB16B51ED400B8B8C0 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = ru; path = ru.lproj/Credits.rtf; sourceTree = ""; }; 96 | 68CCA33F1699145E0081001E /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; 97 | 883AB19717C377E900F835CA /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Credits.rtf"; sourceTree = ""; }; 98 | 883AB19817C377E900F835CA /* zh-Hant-TW */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = ""; }; 99 | C24C463B15E7DC8A00DC943D /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 100 | D53375E415DC43380077CEF6 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 101 | D53375FC15DC44620077CEF6 /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; 102 | E5F79AEE15F1002500F23122 /* pl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; 103 | F018553D15E460A500AC6173 /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; 104 | /* End PBXFileReference section */ 105 | 106 | /* Begin PBXFrameworksBuildPhase section */ 107 | 2841C7D915C91CC100F4F15F /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | 280E9EBD15E0D18C00530934 /* QuartzCore.framework in Frameworks */, 112 | 28FEDA5E15DA5C9D00320B72 /* ServiceManagement.framework in Frameworks */, 113 | 2841C7FE15C91CEF00F4F15F /* IOKit.framework in Frameworks */, 114 | 2841C7E115C91CC100F4F15F /* Cocoa.framework in Frameworks */, 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | /* End PBXFrameworksBuildPhase section */ 119 | 120 | /* Begin PBXGroup section */ 121 | 2841C7D115C91CC100F4F15F = { 122 | isa = PBXGroup; 123 | children = ( 124 | 28FEDA5F15DA5EB200320B72 /* LaunchAtLoginHelper.xcodeproj */, 125 | 2841C7E615C91CC100F4F15F /* Battery Time Remaining */, 126 | 2841C7DF15C91CC100F4F15F /* Frameworks */, 127 | 2841C7DD15C91CC100F4F15F /* Products */, 128 | ); 129 | sourceTree = ""; 130 | }; 131 | 2841C7DD15C91CC100F4F15F /* Products */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 2841C7DC15C91CC100F4F15F /* Battery Time Remaining 2.app */, 135 | ); 136 | name = Products; 137 | sourceTree = ""; 138 | }; 139 | 2841C7DF15C91CC100F4F15F /* Frameworks */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 280E9EBC15E0D18C00530934 /* QuartzCore.framework */, 143 | 28FEDA5D15DA5C9D00320B72 /* ServiceManagement.framework */, 144 | 2841C7FD15C91CEF00F4F15F /* IOKit.framework */, 145 | 2841C7E015C91CC100F4F15F /* Cocoa.framework */, 146 | 2841C7E215C91CC100F4F15F /* Other Frameworks */, 147 | ); 148 | name = Frameworks; 149 | sourceTree = ""; 150 | }; 151 | 2841C7E215C91CC100F4F15F /* Other Frameworks */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 2841C7E315C91CC100F4F15F /* AppKit.framework */, 155 | 2841C7E415C91CC100F4F15F /* CoreData.framework */, 156 | 2841C7E515C91CC100F4F15F /* Foundation.framework */, 157 | ); 158 | name = "Other Frameworks"; 159 | sourceTree = ""; 160 | }; 161 | 2841C7E615C91CC100F4F15F /* Battery Time Remaining */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 2841C7F215C91CC100F4F15F /* AppDelegate.h */, 165 | 2841C7F315C91CC100F4F15F /* AppDelegate.m */, 166 | 28833BAC15D7B90B00741727 /* HttpGet.h */, 167 | 28833BAD15D7B90B00741727 /* HttpGet.m */, 168 | 28527E561622E8A800EE3644 /* ImageFilter.h */, 169 | 28527E571622E8A800EE3644 /* ImageFilter.m */, 170 | 28FEDA6215DA5EB200320B72 /* LLManager.h */, 171 | 28FEDA6315DA5EB200320B72 /* LLManager.m */, 172 | 28FEDA6415DA5EB200320B72 /* LLStrings.h */, 173 | 2841C7E715C91CC100F4F15F /* Supporting Files */, 174 | ); 175 | path = "Battery Time Remaining"; 176 | sourceTree = ""; 177 | }; 178 | 2841C7E715C91CC100F4F15F /* Supporting Files */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 2841C7E815C91CC100F4F15F /* Battery Time Remaining-Info.plist */, 182 | 2841C7EE15C91CC100F4F15F /* Battery Time Remaining-Prefix.pch */, 183 | 28BA61A915D1B63300EDB674 /* Battery Time Remaining.entitlements */, 184 | 28833BB315D7D61100741727 /* build_version */, 185 | 2824942C17AE658B00DD0F74 /* build_version2 */, 186 | 2841C7EF15C91CC100F4F15F /* Credits.rtf */, 187 | 28BA61A715D1B12500EDB674 /* icon.icns */, 188 | 28473E3B1787505700D6CC7C /* LICENSE */, 189 | D53375E315DC43380077CEF6 /* Localizable.strings */, 190 | 2841C7EC15C91CC100F4F15F /* main.m */, 191 | 28833BB115D7D4FF00741727 /* Readme.md */, 192 | ); 193 | name = "Supporting Files"; 194 | sourceTree = ""; 195 | }; 196 | 28FEDA6015DA5EB200320B72 /* Products */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | 28FEDA6B15DA5EB200320B72 /* LaunchAtLoginHelper.app */, 200 | ); 201 | name = Products; 202 | sourceTree = ""; 203 | }; 204 | /* End PBXGroup section */ 205 | 206 | /* Begin PBXNativeTarget section */ 207 | 2841C7DB15C91CC100F4F15F /* Battery Time Remaining */ = { 208 | isa = PBXNativeTarget; 209 | buildConfigurationList = 2841C7FA15C91CC200F4F15F /* Build configuration list for PBXNativeTarget "Battery Time Remaining" */; 210 | buildPhases = ( 211 | 2841C7D815C91CC100F4F15F /* Sources */, 212 | 2841C7D915C91CC100F4F15F /* Frameworks */, 213 | 2841C7DA15C91CC100F4F15F /* Resources */, 214 | 28FEDA5B15DA5B5900320B72 /* CopyFiles */, 215 | ); 216 | buildRules = ( 217 | ); 218 | dependencies = ( 219 | 28FEDA6D15DA5EC200320B72 /* PBXTargetDependency */, 220 | ); 221 | name = "Battery Time Remaining"; 222 | productName = "Battery Time Remaining"; 223 | productReference = 2841C7DC15C91CC100F4F15F /* Battery Time Remaining 2.app */; 224 | productType = "com.apple.product-type.application"; 225 | }; 226 | /* End PBXNativeTarget section */ 227 | 228 | /* Begin PBXProject section */ 229 | 2841C7D315C91CC100F4F15F /* Project object */ = { 230 | isa = PBXProject; 231 | attributes = { 232 | LastUpgradeCheck = 0800; 233 | ORGANIZATIONNAME = "Han Lin Yap"; 234 | TargetAttributes = { 235 | 2841C7DB15C91CC100F4F15F = { 236 | DevelopmentTeam = 9K37AQ8P25; 237 | }; 238 | }; 239 | }; 240 | buildConfigurationList = 2841C7D615C91CC100F4F15F /* Build configuration list for PBXProject "Battery Time Remaining" */; 241 | compatibilityVersion = "Xcode 3.2"; 242 | developmentRegion = English; 243 | hasScannedForEncodings = 0; 244 | knownRegions = ( 245 | en, 246 | nl, 247 | sv, 248 | fr, 249 | sk, 250 | pt, 251 | pl, 252 | "zh-Hans", 253 | it, 254 | ko, 255 | es, 256 | ru, 257 | "zh-Hant-TW", 258 | ); 259 | mainGroup = 2841C7D115C91CC100F4F15F; 260 | productRefGroup = 2841C7DD15C91CC100F4F15F /* Products */; 261 | projectDirPath = ""; 262 | projectReferences = ( 263 | { 264 | ProductGroup = 28FEDA6015DA5EB200320B72 /* Products */; 265 | ProjectRef = 28FEDA5F15DA5EB200320B72 /* LaunchAtLoginHelper.xcodeproj */; 266 | }, 267 | ); 268 | projectRoot = ""; 269 | targets = ( 270 | 2841C7DB15C91CC100F4F15F /* Battery Time Remaining */, 271 | ); 272 | }; 273 | /* End PBXProject section */ 274 | 275 | /* Begin PBXReferenceProxy section */ 276 | 28FEDA6B15DA5EB200320B72 /* LaunchAtLoginHelper.app */ = { 277 | isa = PBXReferenceProxy; 278 | fileType = wrapper.application; 279 | path = LaunchAtLoginHelper.app; 280 | remoteRef = 28FEDA6A15DA5EB200320B72 /* PBXContainerItemProxy */; 281 | sourceTree = BUILT_PRODUCTS_DIR; 282 | }; 283 | /* End PBXReferenceProxy section */ 284 | 285 | /* Begin PBXResourcesBuildPhase section */ 286 | 2841C7DA15C91CC100F4F15F /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | 2841C7F115C91CC100F4F15F /* Credits.rtf in Resources */, 291 | 28BA61A815D1B12500EDB674 /* icon.icns in Resources */, 292 | 28833BB215D7D4FF00741727 /* Readme.md in Resources */, 293 | 28833BB415D7D61100741727 /* build_version in Resources */, 294 | D53375E515DC43380077CEF6 /* Localizable.strings in Resources */, 295 | 28473E3C1787505700D6CC7C /* LICENSE in Resources */, 296 | 2824942D17AE658B00DD0F74 /* build_version2 in Resources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXResourcesBuildPhase section */ 301 | 302 | /* Begin PBXSourcesBuildPhase section */ 303 | 2841C7D815C91CC100F4F15F /* Sources */ = { 304 | isa = PBXSourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | 28FEDA6F15DA5F8C00320B72 /* LLManager.m in Sources */, 308 | 2841C7ED15C91CC100F4F15F /* main.m in Sources */, 309 | 2841C7F415C91CC100F4F15F /* AppDelegate.m in Sources */, 310 | 28833BAE15D7B90B00741727 /* HttpGet.m in Sources */, 311 | 28527E581622E8A800EE3644 /* ImageFilter.m in Sources */, 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | }; 315 | /* End PBXSourcesBuildPhase section */ 316 | 317 | /* Begin PBXTargetDependency section */ 318 | 28FEDA6D15DA5EC200320B72 /* PBXTargetDependency */ = { 319 | isa = PBXTargetDependency; 320 | name = LaunchAtLoginHelper; 321 | targetProxy = 28FEDA6C15DA5EC200320B72 /* PBXContainerItemProxy */; 322 | }; 323 | /* End PBXTargetDependency section */ 324 | 325 | /* Begin PBXVariantGroup section */ 326 | 2841C7EF15C91CC100F4F15F /* Credits.rtf */ = { 327 | isa = PBXVariantGroup; 328 | children = ( 329 | 2841C7F015C91CC100F4F15F /* en */, 330 | 2CDF24CB16B51ED400B8B8C0 /* ru */, 331 | 883AB19717C377E900F835CA /* zh-Hant-TW */, 332 | ); 333 | name = Credits.rtf; 334 | sourceTree = ""; 335 | }; 336 | D53375E315DC43380077CEF6 /* Localizable.strings */ = { 337 | isa = PBXVariantGroup; 338 | children = ( 339 | D53375E415DC43380077CEF6 /* en */, 340 | D53375FC15DC44620077CEF6 /* nl */, 341 | 288305DF15DD005C0064D457 /* sv */, 342 | F018553D15E460A500AC6173 /* fr */, 343 | C24C463B15E7DC8A00DC943D /* de */, 344 | 28369FFA15EE902E005957A0 /* sk */, 345 | E5F79AEE15F1002500F23122 /* pl */, 346 | 28C8E25415F684CD009AA6DB /* pt */, 347 | 2824FFEF1611A5D500CD3140 /* zh-Hans */, 348 | 28563BBE1621A34500960191 /* it */, 349 | 2822133616369BB70019A3EE /* ko */, 350 | 68CCA33F1699145E0081001E /* es */, 351 | 2CDF24CA16B51EC500B8B8C0 /* ru */, 352 | 883AB19817C377E900F835CA /* zh-Hant-TW */, 353 | ); 354 | name = Localizable.strings; 355 | sourceTree = ""; 356 | }; 357 | /* End PBXVariantGroup section */ 358 | 359 | /* Begin XCBuildConfiguration section */ 360 | 2841C7F815C91CC200F4F15F /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 366 | CLANG_ENABLE_OBJC_ARC = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_CONSTANT_CONVERSION = YES; 369 | CLANG_WARN_EMPTY_BODY = YES; 370 | CLANG_WARN_ENUM_CONVERSION = YES; 371 | CLANG_WARN_INFINITE_RECURSION = YES; 372 | CLANG_WARN_INT_CONVERSION = YES; 373 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 374 | CLANG_WARN_UNREACHABLE_CODE = YES; 375 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 376 | COPY_PHASE_STRIP = NO; 377 | ENABLE_STRICT_OBJC_MSGSEND = YES; 378 | ENABLE_TESTABILITY = YES; 379 | GCC_C_LANGUAGE_STANDARD = gnu99; 380 | GCC_DYNAMIC_NO_PIC = NO; 381 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 382 | GCC_NO_COMMON_BLOCKS = YES; 383 | GCC_OPTIMIZATION_LEVEL = 0; 384 | GCC_PREPROCESSOR_DEFINITIONS = ( 385 | "DEBUG=1", 386 | "$(inherited)", 387 | ); 388 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 389 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 390 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 391 | GCC_WARN_UNDECLARED_SELECTOR = YES; 392 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 393 | GCC_WARN_UNUSED_FUNCTION = YES; 394 | GCC_WARN_UNUSED_VARIABLE = YES; 395 | MACOSX_DEPLOYMENT_TARGET = 10.12; 396 | ONLY_ACTIVE_ARCH = YES; 397 | SDKROOT = macosx; 398 | }; 399 | name = Debug; 400 | }; 401 | 2841C7F915C91CC200F4F15F /* Release */ = { 402 | isa = XCBuildConfiguration; 403 | buildSettings = { 404 | ALWAYS_SEARCH_USER_PATHS = NO; 405 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 406 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 407 | CLANG_ENABLE_OBJC_ARC = YES; 408 | CLANG_WARN_BOOL_CONVERSION = YES; 409 | CLANG_WARN_CONSTANT_CONVERSION = YES; 410 | CLANG_WARN_EMPTY_BODY = YES; 411 | CLANG_WARN_ENUM_CONVERSION = YES; 412 | CLANG_WARN_INFINITE_RECURSION = YES; 413 | CLANG_WARN_INT_CONVERSION = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNREACHABLE_CODE = YES; 416 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 417 | COPY_PHASE_STRIP = YES; 418 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 419 | ENABLE_STRICT_OBJC_MSGSEND = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 424 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 425 | GCC_WARN_UNDECLARED_SELECTOR = YES; 426 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 427 | GCC_WARN_UNUSED_FUNCTION = YES; 428 | GCC_WARN_UNUSED_VARIABLE = YES; 429 | MACOSX_DEPLOYMENT_TARGET = 10.12; 430 | SDKROOT = macosx; 431 | }; 432 | name = Release; 433 | }; 434 | 2841C7FB15C91CC200F4F15F /* Debug */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | CODE_SIGN_ENTITLEMENTS = "Battery Time Remaining/Battery Time Remaining.entitlements"; 438 | CODE_SIGN_IDENTITY = "Mac Developer"; 439 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; 440 | COMBINE_HIDPI_IMAGES = YES; 441 | DEVELOPMENT_TEAM = 9K37AQ8P25; 442 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 443 | GCC_PREFIX_HEADER = "Battery Time Remaining/Battery Time Remaining-Prefix.pch"; 444 | INFOPLIST_FILE = "Battery Time Remaining/Battery Time Remaining-Info.plist"; 445 | MACOSX_DEPLOYMENT_TARGET = 10.12; 446 | PRODUCT_BUNDLE_IDENTIFIER = "com.codler.Battery-Time-Remaining2"; 447 | PRODUCT_NAME = "Battery Time Remaining 2"; 448 | PROVISIONING_PROFILE = ""; 449 | WRAPPER_EXTENSION = app; 450 | }; 451 | name = Debug; 452 | }; 453 | 2841C7FC15C91CC200F4F15F /* Release */ = { 454 | isa = XCBuildConfiguration; 455 | buildSettings = { 456 | CODE_SIGN_ENTITLEMENTS = "Battery Time Remaining/Battery Time Remaining.entitlements"; 457 | CODE_SIGN_IDENTITY = "Mac Developer"; 458 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; 459 | COMBINE_HIDPI_IMAGES = YES; 460 | DEVELOPMENT_TEAM = 9K37AQ8P25; 461 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 462 | GCC_PREFIX_HEADER = "Battery Time Remaining/Battery Time Remaining-Prefix.pch"; 463 | INFOPLIST_FILE = "Battery Time Remaining/Battery Time Remaining-Info.plist"; 464 | MACOSX_DEPLOYMENT_TARGET = 10.12; 465 | PRODUCT_BUNDLE_IDENTIFIER = "com.codler.Battery-Time-Remaining2"; 466 | PRODUCT_NAME = "Battery Time Remaining 2"; 467 | PROVISIONING_PROFILE = ""; 468 | WRAPPER_EXTENSION = app; 469 | }; 470 | name = Release; 471 | }; 472 | /* End XCBuildConfiguration section */ 473 | 474 | /* Begin XCConfigurationList section */ 475 | 2841C7D615C91CC100F4F15F /* Build configuration list for PBXProject "Battery Time Remaining" */ = { 476 | isa = XCConfigurationList; 477 | buildConfigurations = ( 478 | 2841C7F815C91CC200F4F15F /* Debug */, 479 | 2841C7F915C91CC200F4F15F /* Release */, 480 | ); 481 | defaultConfigurationIsVisible = 0; 482 | defaultConfigurationName = Release; 483 | }; 484 | 2841C7FA15C91CC200F4F15F /* Build configuration list for PBXNativeTarget "Battery Time Remaining" */ = { 485 | isa = XCConfigurationList; 486 | buildConfigurations = ( 487 | 2841C7FB15C91CC200F4F15F /* Debug */, 488 | 2841C7FC15C91CC200F4F15F /* Release */, 489 | ); 490 | defaultConfigurationIsVisible = 0; 491 | defaultConfigurationName = Release; 492 | }; 493 | /* End XCConfigurationList section */ 494 | }; 495 | rootObject = 2841C7D315C91CC100F4F15F /* Project object */; 496 | } 497 | -------------------------------------------------------------------------------- /Battery Time Remaining/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Battery Time Remaining 4 | // 5 | // Created by Han Lin Yap on 2012-08-01. 6 | // Copyright (c) 2013 Han Lin Yap. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "HttpGet.h" 11 | #import "ImageFilter.h" 12 | #import "LLManager.h" 13 | #import 14 | #import 15 | #import 16 | #import 17 | 18 | //#define DEBUG_BATTERY_PERCENT 19 | //#define CHECK_FOR_UPDATE 20 | 21 | // In Apple's battery gauge, the battery icon is rendered further down from the 22 | // top than NSStatusItem does it. Hence we add an extra top offset to get the 23 | // exact same look. 24 | #define EXTRA_TOP_OFFSET 2.0f 25 | 26 | // IOPS notification callback on power source change 27 | static void PowerSourceChanged(void * context) 28 | { 29 | // Update the time remaining text 30 | AppDelegate *self = (__bridge AppDelegate *)context; 31 | [self updateStatusItem]; 32 | } 33 | 34 | @interface AppDelegate () 35 | { 36 | NSDictionary *batteryIcons; 37 | NSTimer *menuUpdateTimer; 38 | NSTimer *optionKeyPressedTimer; 39 | NSString *previousState; 40 | NSTimer *unpluggedTimer; 41 | NSInteger unpluggedTimerCount; 42 | BOOL isDarkMode; 43 | BOOL isMenuOpen; 44 | BOOL isOptionKeyPressed; 45 | BOOL isCapacityWarning; 46 | BOOL showParenthesis; 47 | BOOL showFahrenheit; 48 | BOOL showPercentage; 49 | BOOL hideIcon; 50 | BOOL hideTime; 51 | BOOL iconRight; 52 | } 53 | 54 | - (void)cacheBatteryIcon; 55 | - (NSImage *)loadBatteryIconNamed:(NSString *)iconName; 56 | - (double)convertCelsiusToFahrenheit:(double)celsius; 57 | 58 | @end 59 | 60 | @implementation AppDelegate 61 | 62 | @synthesize statusItem, notifications, previousPercent; 63 | 64 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 65 | { 66 | self.advancedSupported = ([self getAdvancedBatteryInfo] != nil); 67 | [self cacheBatteryIcon]; 68 | isCapacityWarning = NO; 69 | isMenuOpen = NO; 70 | 71 | // Observe Dark mode changes 72 | [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(darkModeChanged:) name:@"AppleInterfaceThemeChangedNotification" object:nil]; 73 | [self updateDarkMode]; 74 | 75 | // Init notification 76 | [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; 77 | [self loadNotificationSetting]; 78 | 79 | // Set default notification settings if not set 80 | if (![self.notifications objectForKey:@"15"]) 81 | { 82 | [self.notifications setValue:[NSNumber numberWithBool:YES] forKey:@"15"]; 83 | } 84 | if (![self.notifications objectForKey:@"100"]) 85 | { 86 | [self.notifications setValue:[NSNumber numberWithBool:YES] forKey:@"100"]; 87 | } 88 | 89 | [self saveNotificationSetting]; 90 | 91 | // Power source menu item 92 | NSMenuItem *psPercentMenu = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Loading…", @"Remaining menuitem") action:nil keyEquivalent:@""]; 93 | [psPercentMenu setTag:kBTRMenuPowerSourcePercent]; 94 | [psPercentMenu setEnabled:NO]; 95 | 96 | NSMenuItem *psStateMenu = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Power source: %@", @"Powersource menuitem"), NSLocalizedString(@"Unknown", @"Powersource state")] action:nil keyEquivalent:@""]; 97 | [psStateMenu setTag:kBTRMenuPowerSourceState]; 98 | [psStateMenu setEnabled:NO]; 99 | 100 | NSMenuItem *psAdvancedMenu = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; 101 | [psAdvancedMenu setTag:kBTRMenuPowerSourceAdvanced]; 102 | [psAdvancedMenu setEnabled:NO]; 103 | [psAdvancedMenu setHidden:![[NSUserDefaults standardUserDefaults] boolForKey:@"advanced"]]; 104 | 105 | // Start at login menu item 106 | NSMenuItem *startAtLoginMenu = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Start at login", @"Start at login setting") action:@selector(toggleStartAtLogin:) keyEquivalent:@""]; 107 | [startAtLoginMenu setTag:kBTRMenuStartAtLogin]; 108 | startAtLoginMenu.target = self; 109 | startAtLoginMenu.state = ([LLManager launchAtLogin]) ? NSOnState : NSOffState; 110 | 111 | // Build the notification submenu 112 | NSMenu *notificationSubmenu = [[NSMenu alloc] initWithTitle:@"Notification Menu"]; 113 | for (int i = 5; i <= 100; i = i + 5) 114 | { 115 | BOOL state = [[self.notifications valueForKey:[NSString stringWithFormat:@"%d", i]] boolValue]; 116 | 117 | NSMenuItem *notificationSubmenuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"%d%%", i] action:@selector(toggleNotification:) keyEquivalent:@""]; 118 | notificationSubmenuItem.tag = i; 119 | notificationSubmenuItem.state = (state) ? NSOnState : NSOffState; 120 | [notificationSubmenu addItem:notificationSubmenuItem]; 121 | } 122 | 123 | // Notification menu item 124 | NSMenuItem *notificationMenu = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Notifications", @"Notification menuitem") action:nil keyEquivalent:@""]; 125 | [notificationMenu setTag:kBTRMenuNotification]; 126 | [notificationMenu setSubmenu:notificationSubmenu]; 127 | [notificationMenu setHidden:self.advancedSupported && ![[NSUserDefaults standardUserDefaults] boolForKey:@"advanced"]]; 128 | 129 | // Advanced mode menu item 130 | NSMenuItem *advancedSubmenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Advanced mode", @"Advanced mode setting") action:@selector(toggleAdvanced:) keyEquivalent:@""]; 131 | [advancedSubmenuItem setTag:kBTRMenuAdvanced]; 132 | advancedSubmenuItem.target = self; 133 | advancedSubmenuItem.state = ([[NSUserDefaults standardUserDefaults] boolForKey:@"advanced"]) ? NSOnState : NSOffState; 134 | [advancedSubmenuItem setHidden:!self.advancedSupported]; 135 | 136 | // Time display control menu item 137 | NSMenuItem *parenthesisSubmenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Display time with parentheses", @"Display time with parentheses setting") action:@selector(toggleParenthesis:) keyEquivalent:@""]; 138 | [parenthesisSubmenuItem setTag:kBTRMenuParenthesis]; 139 | parenthesisSubmenuItem.target = self; 140 | showParenthesis = [[NSUserDefaults standardUserDefaults] boolForKey:@"parentheses"]; 141 | parenthesisSubmenuItem.state = (showParenthesis) ? NSOnState : NSOffState; 142 | 143 | // Fahrenheit menu item 144 | NSMenuItem *fahrenheitSubmenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Display Fahrenheit", @"Display Fahrenheit setting") action:@selector(toggleFahrenheit:) keyEquivalent:@""]; 145 | [fahrenheitSubmenuItem setTag:kBTRMenuFahrenheit]; 146 | fahrenheitSubmenuItem.target = self; 147 | showFahrenheit = [[NSUserDefaults standardUserDefaults] boolForKey:@"fahrenheit"]; 148 | fahrenheitSubmenuItem.state = (showFahrenheit) ? NSOnState : NSOffState; 149 | 150 | // Percentage menu item 151 | NSMenuItem *percentageSubmenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Display percentage", @"Display percentage setting") action:@selector(togglePercentage:) keyEquivalent:@""]; 152 | [percentageSubmenuItem setTag:kBTRMenuPercentage]; 153 | percentageSubmenuItem.target = self; 154 | showPercentage = [[NSUserDefaults standardUserDefaults] boolForKey:@"percentage"]; 155 | percentageSubmenuItem.state = (showPercentage) ? NSOnState : NSOffState; 156 | 157 | // Icon menu item 158 | NSMenuItem *hideIconSubmenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Hide icon", @"Hide icon setting") action:@selector(toggleHideIcon:) keyEquivalent:@""]; 159 | [hideIconSubmenuItem setTag:kBTRMenuHideIcon]; 160 | hideIconSubmenuItem.target = self; 161 | hideIcon = [[NSUserDefaults standardUserDefaults] boolForKey:@"hideIcon"]; 162 | hideIconSubmenuItem.state = (hideIcon) ? NSOnState : NSOffState; 163 | 164 | // Time menu item 165 | NSMenuItem *hideTimeSubmenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Hide time", @"Hide time setting") action:@selector(toggleHideTime:) keyEquivalent:@""]; 166 | [hideTimeSubmenuItem setTag:kBTRMenuHideTime]; 167 | hideTimeSubmenuItem.target = self; 168 | hideTime = [[NSUserDefaults standardUserDefaults] boolForKey:@"hideTime"]; 169 | hideTimeSubmenuItem.state = (hideTime) ? NSOnState : NSOffState; 170 | 171 | // Icon right menu item 172 | NSMenuItem *iconRightSubmenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place icon on right side", @"Place icon on right side setting") action:@selector(toggleIconRight:) keyEquivalent:@""]; 173 | [iconRightSubmenuItem setTag:kBTRMenuIconRight]; 174 | iconRightSubmenuItem.target = self; 175 | iconRight = [[NSUserDefaults standardUserDefaults] boolForKey:@"iconRight"]; 176 | iconRightSubmenuItem.state = (iconRight) ? NSOnState : NSOffState; 177 | 178 | // Build the setting submenu 179 | NSMenu *settingSubmenu = [[NSMenu alloc] initWithTitle:@"Setting Menu"]; 180 | [settingSubmenu addItem:advancedSubmenuItem]; 181 | [settingSubmenu addItem:parenthesisSubmenuItem]; 182 | [settingSubmenu addItem:fahrenheitSubmenuItem]; 183 | [settingSubmenu addItem:percentageSubmenuItem]; 184 | [settingSubmenu addItem:hideIconSubmenuItem]; 185 | [settingSubmenu addItem:hideTimeSubmenuItem]; 186 | [settingSubmenu addItem:iconRightSubmenuItem]; 187 | 188 | // Settings menu item 189 | NSMenuItem *settingMenu = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Settings", @"Settings menuitem") action:nil keyEquivalent:@""]; 190 | [settingMenu setTag:kBTRMenuSetting]; 191 | [settingMenu setSubmenu:settingSubmenu]; 192 | 193 | #ifdef CHECK_FOR_UPDATE 194 | // Updater menu 195 | NSMenuItem *updaterMenu = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Checking for updates…", @"Update menuitem") action:nil keyEquivalent:@""]; 196 | [updaterMenu setTag:kBTRMenuUpdater]; 197 | [updaterMenu setEnabled:NO]; 198 | #endif 199 | 200 | // Build the statusbar menu 201 | NSMenu *statusBarMenu = [[NSMenu alloc] initWithTitle:@"Status Menu"]; 202 | [statusBarMenu setDelegate:self]; 203 | 204 | [statusBarMenu addItem:psPercentMenu]; 205 | [statusBarMenu addItem:psStateMenu]; 206 | [statusBarMenu addItem:psAdvancedMenu]; 207 | [statusBarMenu addItem:[NSMenuItem separatorItem]]; // Separator 208 | 209 | [statusBarMenu addItem:startAtLoginMenu]; 210 | [statusBarMenu addItem:notificationMenu]; 211 | [statusBarMenu addItem:settingMenu]; 212 | [statusBarMenu addItem:[NSMenuItem separatorItem]]; // Separator 213 | 214 | [statusBarMenu addItemWithTitle:NSLocalizedString(@"Energy Saver Preferences…", @"Open Energy Saver Preferences menuitem") action:@selector(openEnergySaverPreference:) keyEquivalent:@""]; 215 | [statusBarMenu addItem:[NSMenuItem separatorItem]]; // Separator 216 | 217 | #ifdef CHECK_FOR_UPDATE 218 | [statusBarMenu addItem:updaterMenu]; 219 | [statusBarMenu addItem:[NSMenuItem separatorItem]]; // Separator 220 | #endif 221 | 222 | [statusBarMenu addItemWithTitle:NSLocalizedString(@"Quit", @"Quit menuitem") action:@selector(terminate:) keyEquivalent:@""]; 223 | 224 | // Create the status item and set initial text 225 | statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; 226 | statusItem.highlightMode = YES; 227 | statusItem.menu = statusBarMenu; 228 | [self updateStatusItem]; 229 | 230 | // Capture Power Source updates and make sure our callback is called 231 | CFRunLoopSourceRef loop = IOPSNotificationCreateRunLoopSource(PowerSourceChanged, (__bridge void *)self); 232 | CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, kCFRunLoopDefaultMode); 233 | CFRelease(loop); 234 | } 235 | 236 | - (void)unpluggedTimerTick 237 | { 238 | // increase counter by 60 seconds each tick of corresponding timer 239 | unpluggedTimerCount += 60; 240 | } 241 | 242 | - (void)updateStatusItem 243 | { 244 | // reset warning state; new state will be calculated here anyway 245 | isCapacityWarning = NO; 246 | 247 | // Get the estimated time remaining 248 | CFTimeInterval timeRemaining = IOPSGetTimeRemainingEstimate(); 249 | 250 | // Get list of power sources 251 | CFTypeRef psBlob = IOPSCopyPowerSourcesInfo(); 252 | CFArrayRef psList = IOPSCopyPowerSourcesList(psBlob); 253 | 254 | // Loop through the list of power sources 255 | CFIndex count = CFArrayGetCount(psList); 256 | for (CFIndex i = 0; i < count; i++) 257 | { 258 | CFTypeRef powersource = CFArrayGetValueAtIndex(psList, i); 259 | CFDictionaryRef description = IOPSGetPowerSourceDescription(psBlob, powersource); 260 | 261 | // Skip if not present 262 | if (CFDictionaryGetValue(description, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse) 263 | { 264 | continue; 265 | } 266 | 267 | // Calculate the percent 268 | NSNumber *currentBatteryCapacity = CFDictionaryGetValue(description, CFSTR(kIOPSCurrentCapacityKey)); 269 | NSNumber *maxBatteryCapacity = CFDictionaryGetValue(description, CFSTR(kIOPSMaxCapacityKey)); 270 | 271 | self.currentPercent = (int)[currentBatteryCapacity doubleValue] / [maxBatteryCapacity doubleValue] * 100; 272 | 273 | NSString *psState = CFDictionaryGetValue(description, CFSTR(kIOPSPowerSourceStateKey)); 274 | 275 | NSString *psStateTranslated = ([psState isEqualToString:(NSString *)CFSTR(kIOPSBatteryPowerValue)]) ? 276 | NSLocalizedString(@"Battery Power", @"Powersource state") : 277 | ([psState isEqualToString:(NSString *)CFSTR(kIOPSACPowerValue)]) ? 278 | NSLocalizedString(@"AC Power", @"Powersource state") : 279 | NSLocalizedString(@"Off Line", @"Powersource state"); 280 | 281 | [self.statusItem.menu itemWithTag:kBTRMenuPowerSourceState].title = [NSString stringWithFormat:NSLocalizedString(@"Power source: %@", @"Powersource menuitem"), psStateTranslated]; 282 | 283 | // Figure out what's changed: AC to Battery or vise versa 284 | if(![previousState isEqualToString:psState]) 285 | { 286 | if([psState isEqualToString:(NSString *)CFSTR(kIOPSBatteryPowerValue)]) 287 | { 288 | // power source changed from AC to battery 289 | // start counting time we spend on battery 290 | unpluggedTimerCount = 0; 291 | unpluggedTimer = [NSTimer 292 | timerWithTimeInterval:60 293 | target:self 294 | selector:@selector(unpluggedTimerTick) 295 | userInfo:nil 296 | repeats:YES]; 297 | [[NSRunLoop currentRunLoop] addTimer:unpluggedTimer forMode:NSRunLoopCommonModes]; 298 | } 299 | else if([psState isEqualToString:(NSString *)CFSTR(kIOPSACPowerValue)]) 300 | { 301 | // power source changed from battery to AC 302 | // stop timer and reset 303 | [unpluggedTimer invalidate]; 304 | unpluggedTimer = nil; 305 | } 306 | previousState = psState; 307 | } 308 | 309 | // Still calculating the estimated time remaining... 310 | // Fixes #22 - state after reboot 311 | if ([psState isEqualToString:(NSString *)CFSTR(kIOPSBatteryPowerValue)] && CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) == kCFBooleanFalse && (kIOPSTimeRemainingUnknown == timeRemaining || kIOPSTimeRemainingUnlimited == timeRemaining)) 312 | { 313 | [self setStatusBarImage:[self getBatteryIconPercent:self.currentPercent] title:NSLocalizedString(@"Calculating…", @"Calculating sidetext")]; 314 | } 315 | // We're connected to an unlimited power source (AC adapter probably) 316 | else if (kIOPSTimeRemainingUnlimited == timeRemaining) 317 | { 318 | // Check if the battery is charging atm 319 | if (CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) == kCFBooleanTrue) 320 | { 321 | CFNumberRef timeToChargeNum = CFDictionaryGetValue(description, CFSTR(kIOPSTimeToFullChargeKey)); 322 | int timeTilCharged = [(__bridge NSNumber *)timeToChargeNum intValue]; 323 | 324 | if (timeTilCharged > 0) 325 | { 326 | // Calculate the hour/minutes 327 | NSInteger hour = timeTilCharged / 60; 328 | NSInteger minute = timeTilCharged % 60; 329 | 330 | NSString *title = @"%ld:%02ld"; 331 | 332 | // Return the time remaining string 333 | [self setStatusBarImage:[self getBatteryIconNamed:@"BatteryCharging"] title:[NSString stringWithFormat:title, hour, minute]]; 334 | 335 | // Send notification once 336 | if (self.previousPercent != self.currentPercent) 337 | { 338 | if ([[self.notifications valueForKey:[@(self.currentPercent) stringValue]] boolValue] && self.currentPercent >= 50) 339 | { 340 | [self notify:NSLocalizedString(@"Battery Time Remaining", "Battery Time Remaining notification") message:[NSString stringWithFormat:NSLocalizedString(@"%1$ld:%2$02ld left (%3$ld%%)", @"Time remaining left notification"), hour, minute, self.currentPercent]]; 341 | } 342 | self.previousPercent = self.currentPercent; 343 | } 344 | 345 | } 346 | else 347 | { 348 | [self setStatusBarImage:[self getBatteryIconNamed:@"BatteryCharging"] title:NSLocalizedString(@"Calculating…", @"Calculating sidetext")]; 349 | } 350 | } 351 | else 352 | { 353 | // Not charging and on a endless powersource 354 | [self setStatusBarImage:[self getBatteryIconNamed:@"BatteryCharged"] title:nil]; 355 | 356 | NSNumber *currentBatteryCapacity = CFDictionaryGetValue(description, CFSTR(kIOPSCurrentCapacityKey)); 357 | NSNumber *maxBatteryCapacity = CFDictionaryGetValue(description, CFSTR(kIOPSMaxCapacityKey)); 358 | 359 | // Notify user when battery is charged 360 | if ([currentBatteryCapacity intValue] == [maxBatteryCapacity intValue] && 361 | self.previousPercent != self.currentPercent && 362 | [[self.notifications valueForKey:@"100"] boolValue]) 363 | { 364 | 365 | [self notify:NSLocalizedString(@"Charged", @"Charged notification")]; 366 | self.previousPercent = self.currentPercent; 367 | } 368 | } 369 | 370 | } 371 | // Time is known! 372 | else 373 | { 374 | // Calculate the hour/minutes 375 | NSInteger hour = (int)timeRemaining / 3600; 376 | NSInteger minute = (int)timeRemaining % 3600 / 60; 377 | 378 | NSString *title = @"%ld:%02ld"; 379 | 380 | // Return the time remaining string 381 | [self setStatusBarImage:[self getBatteryIconPercent:self.currentPercent] title:[NSString stringWithFormat:title, hour, minute]]; 382 | 383 | // Send notification once 384 | if (self.previousPercent != self.currentPercent) 385 | { 386 | if ([[self.notifications valueForKey:[@(self.currentPercent) stringValue]] boolValue] && self.currentPercent <= 50) 387 | { 388 | [self notify:NSLocalizedString(@"Battery Time Remaining", "Battery Time Remaining notification") message:[NSString stringWithFormat:NSLocalizedString(@"%1$ld:%2$02ld left (%3$ld%%)", @"Time remaining left notification"), hour, minute, self.currentPercent]]; 389 | } 390 | self.previousPercent = self.currentPercent; 391 | } 392 | } 393 | 394 | } 395 | 396 | CFRelease(psList); 397 | CFRelease(psBlob); 398 | } 399 | 400 | - (void)updateStatusItemMenu 401 | { 402 | [self updateStatusItem]; 403 | 404 | // Show power source data in menu 405 | if (self.advancedSupported && ([[self.statusItem.menu itemWithTag:kBTRMenuSetting].submenu itemWithTag:kBTRMenuAdvanced].state == NSOnState || isOptionKeyPressed)) 406 | { 407 | NSDictionary *advancedBatteryInfo = [self getAdvancedBatteryInfo]; 408 | NSDictionary *moreAdvancedBatteryInfo = [self getMoreAdvancedBatteryInfo]; 409 | 410 | // Unit mAh 411 | NSNumber *currentBatteryPower = [advancedBatteryInfo objectForKey:@"Current"]; 412 | // Unit mAh 413 | NSNumber *maxBatteryPower = [advancedBatteryInfo objectForKey:@"Capacity"]; 414 | // Unit mAh 415 | NSNumber *Amperage = [advancedBatteryInfo objectForKey:@"Amperage"]; 416 | // Unit mV 417 | NSNumber *Voltage = [advancedBatteryInfo objectForKey:@"Voltage"]; 418 | NSNumber *cycleCount = [advancedBatteryInfo objectForKey:@"Cycle Count"]; 419 | // Unit Wh 420 | NSNumber *watt = [NSNumber numberWithDouble:[Amperage doubleValue] / 1000 * [Voltage doubleValue] / 1000]; 421 | // Unit Celsius 422 | NSNumber *temperature = [NSNumber numberWithDouble:[[moreAdvancedBatteryInfo objectForKey:@"Temperature"] doubleValue] / 100]; 423 | 424 | [self.statusItem.menu itemWithTag:kBTRMenuPowerSourcePercent].title = [NSString stringWithFormat: NSLocalizedString(@"%ld %% left ( %ld/%ld mAh )", @"Advanced percentage left menuitem"), self.currentPercent, [currentBatteryPower integerValue], [maxBatteryPower integerValue]]; 425 | 426 | // time since unplugged 427 | NSString *timeSinceUnplugged; 428 | if (unpluggedTimer != nil) 429 | { 430 | timeSinceUnplugged = NSLocalizedString(@"Time since unplugged: %1$ld:%2$02ld", @"Advanced battery info menuitem"); 431 | } 432 | else 433 | { 434 | timeSinceUnplugged = NSLocalizedString(@"On battery while last unplugged: %1$ld:%2$02ld", @"Advanced battery info menuitem"); 435 | } 436 | NSString *timeSinceUnpluggedTranslated = [NSString stringWithFormat:timeSinceUnplugged, 437 | unpluggedTimerCount / 3600, 438 | unpluggedTimerCount % 3600 / 60]; 439 | 440 | // Temperature 441 | NSString *fahrenheitTranslated; 442 | if (showFahrenheit) { 443 | fahrenheitTranslated = [NSString stringWithFormat:NSLocalizedString(@"Temperature: %.1f°F", @"Advanced battery info menuitem"), [self convertCelsiusToFahrenheit:[temperature doubleValue]]]; 444 | } 445 | else 446 | { 447 | fahrenheitTranslated = [NSString stringWithFormat:NSLocalizedString(@"Temperature: %.1f°C", @"Advanced battery info menuitem"), [temperature doubleValue]]; 448 | } 449 | 450 | // Each item in array will be a row in menu 451 | NSArray *advancedBatteryInfoTexts = [NSArray arrayWithObjects: 452 | [NSString stringWithFormat:NSLocalizedString(@"Cycle count: %ld", @"Advanced battery info menuitem"), [cycleCount integerValue]], 453 | [NSString stringWithFormat:NSLocalizedString(@"Power usage: %.2f Watt", @"Advanced battery info menuitem"), [watt doubleValue]], 454 | timeSinceUnpluggedTranslated, 455 | fahrenheitTranslated, 456 | nil]; 457 | 458 | NSDictionary *advancedAttributedStyle = [NSDictionary dictionaryWithObjectsAndKeys: 459 | // Font 460 | [NSFont systemFontOfSize:[NSFont systemFontSize]+1.f], 461 | NSFontAttributeName, 462 | // Text color 463 | [NSColor disabledControlTextColor], 464 | NSForegroundColorAttributeName, 465 | nil]; 466 | 467 | NSAttributedString *advancedAttributedTitle = [[NSAttributedString alloc] initWithString:[advancedBatteryInfoTexts componentsJoinedByString:@"\n"] attributes:advancedAttributedStyle]; 468 | 469 | [self.statusItem.menu itemWithTag:kBTRMenuPowerSourceAdvanced].attributedTitle = advancedAttributedTitle; 470 | } 471 | else 472 | { 473 | [self.statusItem.menu itemWithTag:kBTRMenuPowerSourcePercent].title = [NSString stringWithFormat: NSLocalizedString(@"%ld %% left", @"Percentage left menuitem"), self.currentPercent]; 474 | } 475 | 476 | } 477 | 478 | - (void)setStatusBarImage:(NSImage *)image title:(NSString *)title 479 | { 480 | // Title 481 | NSMutableDictionary *attributedStyle = [NSMutableDictionary dictionaryWithObjectsAndKeys: 482 | // Font 483 | [NSFont menuFontOfSize:12.0f], 484 | NSFontAttributeName, 485 | nil]; 486 | 487 | if (hideTime) 488 | { 489 | title = nil; 490 | } 491 | 492 | if (showPercentage) 493 | { 494 | if (title != nil) 495 | { 496 | title = [NSString stringWithFormat:@"%ld %% %@", self.currentPercent, title]; 497 | } 498 | else 499 | { 500 | title = [NSString stringWithFormat:@"%ld %%", self.currentPercent]; 501 | } 502 | } 503 | 504 | if (title != nil) 505 | { 506 | if (showParenthesis) 507 | { 508 | title = [NSString stringWithFormat:@"(%@)", title]; 509 | } 510 | 511 | title = [NSString stringWithFormat:@" %@", title]; 512 | } 513 | else 514 | { 515 | title = @""; 516 | } 517 | 518 | NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:title attributes:attributedStyle]; 519 | self.statusItem.attributedTitle = attributedTitle; 520 | 521 | // Force show icon if no text 522 | if (!showPercentage && hideTime) 523 | { 524 | hideIcon = NO; 525 | } 526 | 527 | // Image 528 | if (!hideIcon) 529 | { 530 | [image setTemplate:(isMenuOpen || !isCapacityWarning)]; 531 | [self.statusItem setImage:image]; 532 | 533 | if (iconRight) { 534 | [self.statusItem.button setImagePosition:NSImageRight]; 535 | } else { 536 | [self.statusItem.button setImagePosition:NSImageLeft]; 537 | } 538 | } 539 | else 540 | { 541 | [self.statusItem setImage:nil]; 542 | } 543 | } 544 | 545 | - (NSDictionary *)getAdvancedBatteryInfo 546 | { 547 | mach_port_t masterPort; 548 | CFArrayRef batteryInfo; 549 | 550 | if (kIOReturnSuccess == IOMasterPort(MACH_PORT_NULL, &masterPort) && 551 | kIOReturnSuccess == IOPMCopyBatteryInfo(masterPort, &batteryInfo)) 552 | { 553 | return [(__bridge NSArray*)batteryInfo objectAtIndex:0]; 554 | } 555 | return nil; 556 | } 557 | 558 | - (NSDictionary *)getMoreAdvancedBatteryInfo 559 | { 560 | CFMutableDictionaryRef matching, properties = NULL; 561 | io_registry_entry_t entry = 0; 562 | // same as matching = IOServiceMatching("IOPMPowerSource"); 563 | matching = IOServiceNameMatching("AppleSmartBattery"); 564 | entry = IOServiceGetMatchingService(kIOMasterPortDefault, matching); 565 | IORegistryEntryCreateCFProperties(entry, &properties, NULL, 0); 566 | return (__bridge NSDictionary *)properties; 567 | //IOObjectRelease(entry); 568 | } 569 | 570 | - (NSImage *)getBatteryIconPercent:(NSInteger)percent 571 | { 572 | #ifdef DEBUG_BATTERY_PERCENT 573 | percent = arc4random() % 101; 574 | #endif 575 | 576 | // Mimic Apple's original battery icon using high resolution artwork 577 | NSImage *batteryOutline = [[self getBatteryIconNamed:@"BatteryEmpty"] copy]; 578 | NSImage *batteryLevelLeft = nil; 579 | NSImage *batteryLevelMiddle = nil; 580 | NSImage *batteryLevelRight = nil; 581 | 582 | if (percent > 15) 583 | { 584 | isCapacityWarning = NO; 585 | // draw black capacity bar 586 | batteryLevelLeft = [self getBatteryIconNamed:@"BatteryLevelCapB-L"]; 587 | batteryLevelMiddle = [self getBatteryIconNamed:@"BatteryLevelCapB-M"]; 588 | batteryLevelRight = [self getBatteryIconNamed:@"BatteryLevelCapB-R"]; 589 | } 590 | else 591 | { 592 | isCapacityWarning = YES; 593 | // draw red capacity bar 594 | batteryLevelLeft = [self getBatteryIconNamed:@"BatteryLevelCapR-L"]; 595 | batteryLevelMiddle = [self getBatteryIconNamed:@"BatteryLevelCapR-M"]; 596 | batteryLevelRight = [self getBatteryIconNamed:@"BatteryLevelCapR-R"]; 597 | 598 | if (isDarkMode) 599 | { 600 | batteryOutline = [ImageFilter invertColor:batteryOutline]; 601 | } 602 | } 603 | 604 | const CGFloat drawingUnit = [batteryLevelLeft size].width; 605 | const CGFloat capBarLeftOffset = drawingUnit; 606 | CGFloat capBarHeight = [batteryLevelLeft size].height; 607 | CGFloat capBarTopOffset = ([batteryOutline size].height - EXTRA_TOP_OFFSET - capBarHeight) / 2.0; 608 | CGFloat capBarLength = ceil(percent / 13.0f) * drawingUnit; // max width is 13 units 609 | if (capBarLength <= (2 * drawingUnit)) { capBarLength = (2 * drawingUnit) + 0.1f; } // must be _greater_than_ the end segments 610 | 611 | [batteryOutline lockFocus]; 612 | [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; 613 | NSDrawThreePartImage(NSMakeRect(capBarLeftOffset, capBarTopOffset, capBarLength, capBarHeight), 614 | batteryLevelLeft, batteryLevelMiddle, batteryLevelRight, 615 | NO, 616 | NSCompositeCopy, 617 | 0.94f, 618 | NO); 619 | [batteryOutline unlockFocus]; 620 | 621 | return batteryOutline; 622 | } 623 | 624 | - (NSImage *)getBatteryIconNamed:(NSString *)iconName { 625 | return [batteryIcons objectForKey:iconName]; 626 | } 627 | 628 | - (NSImage *)loadBatteryIconNamed:(NSString *)iconName 629 | { 630 | NSString *fileName = [NSString stringWithFormat:@"/System/Library/PrivateFrameworks/BatteryUIKit.framework/Versions/A/Resources/%@.pdf", iconName]; 631 | return [[NSImage alloc] initWithContentsOfFile:fileName]; 632 | } 633 | 634 | - (void)cacheBatteryIcon { 635 | // special treatment for the BatteryCharging, BatteryCharged, and BatteryEmpty images 636 | // they need to be shifted down by 2points to be in the same position as Apple's 637 | NSImage *imgCharging = [ImageFilter offset:[self loadBatteryIconNamed:@"BatteryCharging"] top:EXTRA_TOP_OFFSET]; 638 | NSImage *imgCharged = [ImageFilter offset:[self loadBatteryIconNamed:@"BatteryChargedAndPlugged"] top:EXTRA_TOP_OFFSET]; 639 | NSImage *imgEmpty = [ImageFilter offset:[self loadBatteryIconNamed:@"BatteryEmpty"] top:EXTRA_TOP_OFFSET]; 640 | 641 | // finally construct the dictionary from which we will retrieve the images at runtime 642 | batteryIcons = [NSDictionary dictionaryWithObjectsAndKeys: 643 | imgCharging, @"BatteryCharging", 644 | imgCharged, @"BatteryCharged", 645 | imgEmpty, @"BatteryEmpty", 646 | [self loadBatteryIconNamed:@"BatteryLevelCapB-L"], @"BatteryLevelCapB-L", 647 | [self loadBatteryIconNamed:@"BatteryLevelCapB-M"], @"BatteryLevelCapB-M", 648 | [self loadBatteryIconNamed:@"BatteryLevelCapB-R"], @"BatteryLevelCapB-R", 649 | [self loadBatteryIconNamed:@"BatteryLevelCapR-L"], @"BatteryLevelCapR-L", 650 | [self loadBatteryIconNamed:@"BatteryLevelCapR-M"], @"BatteryLevelCapR-M", 651 | [self loadBatteryIconNamed:@"BatteryLevelCapR-R"], @"BatteryLevelCapR-R", 652 | nil]; 653 | } 654 | 655 | - (void)openEnergySaverPreference:(id)sender 656 | { 657 | [[NSWorkspace sharedWorkspace] openFile:@"/System/Library/PreferencePanes/EnergySaver.prefPane"]; 658 | } 659 | 660 | - (void)openHomeUrl:(id)sender 661 | { 662 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://yap.nu/battery-time-remaining/"]]; 663 | } 664 | 665 | - (void)openMacAppStore:(id)sender 666 | { 667 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"macappstore://itunes.apple.com/app/id665678267?mt=12"]]; 668 | } 669 | 670 | - (void)promptAutoUpdate:(id)sender 671 | { 672 | NSAlert *alert = [NSAlert new]; 673 | [alert addButtonWithTitle:NSLocalizedString(@"Get Auto Updates", @"Update auto update prompt")]; 674 | [alert addButtonWithTitle:NSLocalizedString(@"No", @"Update auto update prompt")]; 675 | [alert setMessageText:NSLocalizedString(@"Do you want auto updates?", @"Update auto update prompt")]; 676 | NSInteger returnCode = [alert runModal]; 677 | 678 | if (NSAlertSecondButtonReturn == returnCode) 679 | { 680 | [self openHomeUrl:nil]; 681 | } 682 | else 683 | { 684 | [self openMacAppStore:nil]; 685 | } 686 | } 687 | 688 | - (void)toggleStartAtLogin:(id)sender 689 | { 690 | if ([LLManager launchAtLogin]) 691 | { 692 | [LLManager setLaunchAtLogin:NO]; 693 | [self.statusItem.menu itemWithTag:kBTRMenuStartAtLogin].state = NSOffState; 694 | } 695 | else 696 | { 697 | [LLManager setLaunchAtLogin:YES]; 698 | [self.statusItem.menu itemWithTag:kBTRMenuStartAtLogin].state = NSOnState; 699 | } 700 | } 701 | 702 | - (void)toggleAdvanced:(id)sender 703 | { 704 | NSMenuItem *item = sender; 705 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 706 | 707 | if ([defaults boolForKey:@"advanced"]) 708 | { 709 | item.state = NSOffState; 710 | [self showAdvanced:NO]; 711 | [defaults setBool:NO forKey:@"advanced"]; 712 | } 713 | else 714 | { 715 | item.state = NSOnState; 716 | [self showAdvanced:YES]; 717 | [defaults setBool:YES forKey:@"advanced"]; 718 | } 719 | [defaults synchronize]; 720 | 721 | [self updateStatusItem]; 722 | } 723 | 724 | - (void)toggleParenthesis:(id)sender 725 | { 726 | NSMenuItem *item = sender; 727 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 728 | 729 | if ([defaults boolForKey:@"parentheses"]) 730 | { 731 | item.state = NSOffState; 732 | showParenthesis = NO; 733 | [defaults setBool:NO forKey:@"parentheses"]; 734 | } 735 | else 736 | { 737 | item.state = NSOnState; 738 | showParenthesis = YES; 739 | [defaults setBool:YES forKey:@"parentheses"]; 740 | } 741 | [defaults synchronize]; 742 | 743 | [self updateStatusItem]; 744 | } 745 | 746 | - (void)toggleFahrenheit:(id)sender 747 | { 748 | NSMenuItem *item = sender; 749 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 750 | 751 | if ([defaults boolForKey:@"fahrenheit"]) 752 | { 753 | item.state = NSOffState; 754 | showFahrenheit = NO; 755 | [defaults setBool:NO forKey:@"fahrenheit"]; 756 | } 757 | else 758 | { 759 | item.state = NSOnState; 760 | showFahrenheit = YES; 761 | [defaults setBool:YES forKey:@"fahrenheit"]; 762 | } 763 | [defaults synchronize]; 764 | 765 | [self updateStatusItem]; 766 | } 767 | 768 | - (void)togglePercentage:(id)sender 769 | { 770 | NSMenuItem *item = sender; 771 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 772 | 773 | if ([defaults boolForKey:@"percentage"]) 774 | { 775 | item.state = NSOffState; 776 | showPercentage = NO; 777 | [defaults setBool:NO forKey:@"percentage"]; 778 | } 779 | else 780 | { 781 | item.state = NSOnState; 782 | showPercentage = YES; 783 | [defaults setBool:YES forKey:@"percentage"]; 784 | } 785 | [defaults synchronize]; 786 | 787 | [self updateStatusItem]; 788 | } 789 | 790 | - (void)toggleHideIcon:(id)sender 791 | { 792 | NSMenuItem *item = sender; 793 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 794 | 795 | if ([defaults boolForKey:@"hideIcon"]) 796 | { 797 | item.state = NSOffState; 798 | hideIcon = NO; 799 | [defaults setBool:NO forKey:@"hideIcon"]; 800 | } 801 | else 802 | { 803 | item.state = NSOnState; 804 | hideIcon = YES; 805 | [defaults setBool:YES forKey:@"hideIcon"]; 806 | } 807 | [defaults synchronize]; 808 | 809 | [self updateStatusItem]; 810 | } 811 | 812 | - (void)toggleHideTime:(id)sender 813 | { 814 | NSMenuItem *item = sender; 815 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 816 | 817 | if ([defaults boolForKey:@"hideTime"]) 818 | { 819 | item.state = NSOffState; 820 | hideTime = NO; 821 | [defaults setBool:NO forKey:@"hideTime"]; 822 | } 823 | else 824 | { 825 | item.state = NSOnState; 826 | hideTime = YES; 827 | [defaults setBool:YES forKey:@"hideTime"]; 828 | } 829 | [defaults synchronize]; 830 | 831 | [self updateStatusItem]; 832 | } 833 | 834 | - (void)toggleIconRight:(id)sender 835 | { 836 | NSMenuItem *item = sender; 837 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 838 | 839 | if ([defaults boolForKey:@"iconRight"]) 840 | { 841 | item.state = NSOffState; 842 | iconRight = NO; 843 | [defaults setBool:NO forKey:@"iconRight"]; 844 | } 845 | else 846 | { 847 | item.state = NSOnState; 848 | iconRight = YES; 849 | [defaults setBool:YES forKey:@"iconRight"]; 850 | } 851 | [defaults synchronize]; 852 | 853 | [self updateStatusItem]; 854 | } 855 | 856 | - (void)notify:(NSString *)message 857 | { 858 | [self notify:@"Battery Time Remaining" message:message]; 859 | } 860 | 861 | - (void)notify:(NSString *)title message:(NSString *)message 862 | { 863 | NSUserNotification *notification = [[NSUserNotification alloc] init]; 864 | [notification setTitle:title]; 865 | [notification setInformativeText:message]; 866 | [notification setSoundName:NSUserNotificationDefaultSoundName]; 867 | NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; 868 | [center scheduleNotification:notification]; 869 | } 870 | 871 | - (void)loadNotificationSetting 872 | { 873 | // Fetch user settings for notifications 874 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 875 | NSDictionary *immutableNotifications = [defaults dictionaryForKey:@"notifications"]; 876 | if (immutableNotifications) 877 | { 878 | self.notifications = [immutableNotifications mutableCopy]; 879 | } 880 | else 881 | { 882 | self.notifications = [NSMutableDictionary new]; 883 | } 884 | } 885 | 886 | - (void)saveNotificationSetting 887 | { 888 | // Save user settings for notifications 889 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 890 | [defaults setValue:self.notifications forKey:@"notifications"]; 891 | [defaults synchronize]; 892 | } 893 | 894 | - (void)toggleNotification:(id)sender 895 | { 896 | // Get menu item 897 | NSMenuItem *item = (NSMenuItem *)sender; 898 | 899 | // Toggle state 900 | item.state = (item.state==NSOnState) ? NSOffState : NSOnState; 901 | 902 | [self.notifications setValue:[NSNumber numberWithBool:(item.state==NSOnState)?YES:NO] forKey:[NSString stringWithFormat:@"%ld", item.tag]]; 903 | 904 | [self saveNotificationSetting]; 905 | } 906 | 907 | - (void)showAdvanced:(BOOL)visible 908 | { 909 | [[self.statusItem.menu itemWithTag:kBTRMenuPowerSourceAdvanced] setHidden:!visible]; 910 | [[self.statusItem.menu itemWithTag:kBTRMenuNotification] setHidden:!visible]; 911 | } 912 | 913 | - (void)optionKeyPressed 914 | { 915 | // http://stackoverflow.com/a/12333909/304894 916 | // Get global modifier key flag, [[NSApp currentEvent] modifierFlags] doesn't update 917 | CGEventRef event = CGEventCreate(NULL); 918 | CGEventFlags flags = CGEventGetFlags(event); 919 | CFRelease(event); 920 | BOOL prevIsOptionKeyPressed = isOptionKeyPressed; 921 | isOptionKeyPressed = (flags & kCGEventFlagMaskAlternate) == kCGEventFlagMaskAlternate; 922 | 923 | // Option key was pressed or released 924 | if (prevIsOptionKeyPressed != isOptionKeyPressed) 925 | { 926 | [self showAdvanced:self.advancedSupported && ([[self.statusItem.menu itemWithTag:kBTRMenuSetting].submenu itemWithTag:kBTRMenuAdvanced].state == NSOnState || isOptionKeyPressed) ]; 927 | [self updateStatusItemMenu]; 928 | } 929 | } 930 | 931 | - (double)convertCelsiusToFahrenheit:(double)celsius 932 | { 933 | return ((celsius * 9.0) / 5.0 + 32.0); 934 | } 935 | 936 | - (void)updateDarkMode 937 | { 938 | NSDictionary *dict = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain]; 939 | id style = [dict objectForKey:@"AppleInterfaceStyle"]; 940 | isDarkMode = ( style && [style isKindOfClass:[NSString class]] && NSOrderedSame == [style caseInsensitiveCompare:@"dark"] ); 941 | } 942 | 943 | - (void)darkModeChanged:(NSNotification *)notif 944 | { 945 | [self updateDarkMode]; 946 | } 947 | 948 | #pragma mark - NSUserNotificationCenterDelegate methods 949 | 950 | // Force show notification 951 | - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification 952 | { 953 | return YES; 954 | } 955 | 956 | - (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification 957 | { 958 | // User has clicked on the notification and will open home URL if newer version is available 959 | if ([[notification informativeText] isEqualToString:NSLocalizedString(@"A newer version is available", @"Update menuitem")]) 960 | { 961 | [self promptAutoUpdate:nil]; 962 | } 963 | } 964 | 965 | #pragma mark - NSMenuDelegate methods 966 | 967 | - (void)menuWillOpen:(NSMenu *)menu 968 | { 969 | isMenuOpen = YES; 970 | [self.statusItem.image setTemplate:YES]; 971 | 972 | [self updateStatusItemMenu]; 973 | 974 | // Detect instant if option key is pressed 975 | optionKeyPressedTimer = [NSTimer timerWithTimeInterval:0.1 976 | target:self 977 | selector:@selector(optionKeyPressed) 978 | userInfo:nil 979 | repeats:YES]; 980 | [[NSRunLoop currentRunLoop] addTimer:optionKeyPressedTimer forMode:NSRunLoopCommonModes]; 981 | 982 | // Update menu every 5 seconds 983 | menuUpdateTimer = [NSTimer timerWithTimeInterval:5 984 | target:self 985 | selector:@selector(updateStatusItemMenu) 986 | userInfo:nil 987 | repeats:YES]; 988 | [[NSRunLoop currentRunLoop] addTimer:menuUpdateTimer forMode:NSRunLoopCommonModes]; 989 | 990 | #ifdef CHECK_FOR_UPDATE 991 | // Update menu 992 | NSMenuItem *updaterMenu = [self.statusItem.menu itemWithTag:kBTRMenuUpdater]; 993 | 994 | // Stop checking if newer version is available 995 | if ([updaterMenu isEnabled]) 996 | { 997 | return; 998 | } 999 | 1000 | // Check for newer version 1001 | [[HttpGet new] url:@"http://yap.nu/battery-time-remaining/build_version2" success:^(NSString *result) { 1002 | NSInteger latestBuildVersion = [result integerValue]; 1003 | NSInteger currentBuildVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] integerValue]; 1004 | NSString *currentBuildVersionText = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; 1005 | 1006 | // Wrong format build version 1007 | if (!latestBuildVersion) 1008 | { 1009 | updaterMenu.title = NSLocalizedString(@"Could not check for updates", @"Update menuitem"); 1010 | return; 1011 | } 1012 | 1013 | // Newer version available 1014 | if (latestBuildVersion > currentBuildVersion) 1015 | { 1016 | updaterMenu.title = NSLocalizedString(@"A newer version is available", @"Update menuitem"); 1017 | [updaterMenu setAction:@selector(promptAutoUpdate:)]; 1018 | [updaterMenu setEnabled:YES]; 1019 | [self notify:NSLocalizedString(@"A newer version is available", @"Update notification")]; 1020 | } 1021 | else 1022 | { 1023 | updaterMenu.title = [NSString stringWithFormat:@"%@ - v%@", NSLocalizedString(@"Up to date", @"Update menuitem"), currentBuildVersionText]; 1024 | } 1025 | } error:^(NSError *error) { 1026 | updaterMenu.title = NSLocalizedString(@"Could not check for updates", @"Update menuitem"); 1027 | }]; 1028 | #endif 1029 | 1030 | } 1031 | 1032 | - (void)menuDidClose:(NSMenu *)menu 1033 | { 1034 | isMenuOpen = NO; 1035 | [self.statusItem.image setTemplate:!isCapacityWarning]; 1036 | 1037 | if ([[self.statusItem.menu itemWithTag:kBTRMenuSetting].submenu itemWithTag:kBTRMenuAdvanced].state == NSOffState) 1038 | { 1039 | [self showAdvanced:NO]; 1040 | } 1041 | 1042 | [menuUpdateTimer invalidate]; 1043 | menuUpdateTimer = nil; 1044 | 1045 | [optionKeyPressedTimer invalidate]; 1046 | optionKeyPressedTimer = nil; 1047 | } 1048 | 1049 | @end 1050 | --------------------------------------------------------------------------------