Permalink
Browse files

Notify user when a newer version of the app is available

  • Loading branch information...
1 parent 9cf2339 commit 39573ee94141ee8f64aa1a3f6961951504e31729 @codler committed Aug 12, 2012
@@ -13,6 +13,9 @@
2841C7F115C91CC100F4F15F /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 2841C7EF15C91CC100F4F15F /* Credits.rtf */; };
2841C7F415C91CC100F4F15F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2841C7F315C91CC100F4F15F /* AppDelegate.m */; };
2841C7FE15C91CEF00F4F15F /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2841C7FD15C91CEF00F4F15F /* IOKit.framework */; };
+ 28833BAE15D7B90B00741727 /* HttpGet.m in Sources */ = {isa = PBXBuildFile; fileRef = 28833BAD15D7B90B00741727 /* HttpGet.m */; };
+ 28833BB215D7D4FF00741727 /* Readme.md in Resources */ = {isa = PBXBuildFile; fileRef = 28833BB115D7D4FF00741727 /* Readme.md */; };
+ 28833BB415D7D61100741727 /* build_version in Resources */ = {isa = PBXBuildFile; fileRef = 28833BB315D7D61100741727 /* build_version */; };
28BA61A815D1B12500EDB674 /* icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 28BA61A715D1B12500EDB674 /* icon.icns */; };
D58C7EF815CD7C6100C07E0C /* StartAtLoginHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = D58C7EF715CD7C6100C07E0C /* StartAtLoginHelper.m */; };
/* End PBXBuildFile section */
@@ -31,6 +34,10 @@
2841C7F215C91CC100F4F15F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
2841C7F315C91CC100F4F15F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
2841C7FD15C91CEF00F4F15F /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
+ 28833BAC15D7B90B00741727 /* HttpGet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HttpGet.h; sourceTree = "<group>"; };
+ 28833BAD15D7B90B00741727 /* HttpGet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HttpGet.m; sourceTree = "<group>"; };
+ 28833BB115D7D4FF00741727 /* Readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Readme.md; sourceTree = SOURCE_ROOT; };
+ 28833BB315D7D61100741727 /* build_version */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = build_version; sourceTree = SOURCE_ROOT; };
28BA61A715D1B12500EDB674 /* icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = icon.icns; path = ../icon.icns; sourceTree = "<group>"; };
28BA61A915D1B63300EDB674 /* Battery Time Remaining.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Battery Time Remaining.entitlements"; sourceTree = "<group>"; };
D58C7EF615CD7C6100C07E0C /* StartAtLoginHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StartAtLoginHelper.h; sourceTree = "<group>"; };
@@ -92,6 +99,8 @@
children = (
2841C7F215C91CC100F4F15F /* AppDelegate.h */,
2841C7F315C91CC100F4F15F /* AppDelegate.m */,
+ 28833BAC15D7B90B00741727 /* HttpGet.h */,
+ 28833BAD15D7B90B00741727 /* HttpGet.m */,
D58C7EF615CD7C6100C07E0C /* StartAtLoginHelper.h */,
D58C7EF715CD7C6100C07E0C /* StartAtLoginHelper.m */,
2841C7E715C91CC100F4F15F /* Supporting Files */,
@@ -102,6 +111,8 @@
2841C7E715C91CC100F4F15F /* Supporting Files */ = {
isa = PBXGroup;
children = (
+ 28833BB315D7D61100741727 /* build_version */,
+ 28833BB115D7D4FF00741727 /* Readme.md */,
28BA61A715D1B12500EDB674 /* icon.icns */,
28BA61A915D1B63300EDB674 /* Battery Time Remaining.entitlements */,
2841C7E815C91CC100F4F15F /* Battery Time Remaining-Info.plist */,
@@ -167,6 +178,8 @@
2841C7EB15C91CC100F4F15F /* InfoPlist.strings in Resources */,
2841C7F115C91CC100F4F15F /* Credits.rtf in Resources */,
28BA61A815D1B12500EDB674 /* icon.icns in Resources */,
+ 28833BB215D7D4FF00741727 /* Readme.md in Resources */,
+ 28833BB415D7D61100741727 /* build_version in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -180,6 +193,7 @@
2841C7ED15C91CC100F4F15F /* main.m in Sources */,
2841C7F415C91CC100F4F15F /* AppDelegate.m in Sources */,
D58C7EF815CD7C6100C07E0C /* StartAtLoginHelper.m in Sources */,
+ 28833BAE15D7B90B00741727 /* HttpGet.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -8,13 +8,14 @@
#import <Cocoa/Cocoa.h>
-@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
+@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate, NSMenuDelegate>
- (void)updateStatusItem;
- (NSImage *)getBatteryIconNamed:(NSString *)iconName;
@property (strong) NSStatusItem *statusItem;
@property (strong) NSMenuItem *startupToggle;
+@property (strong) NSMenuItem *updaterMenu;
@property (nonatomic) NSInteger previousPercent;
@property (strong) NSMutableDictionary *notifications;
@@ -7,6 +7,7 @@
//
#import "AppDelegate.h"
+#import "HttpGet.h"
#import "StartAtLoginHelper.h"
#import <IOKit/ps/IOPowerSources.h>
#import <IOKit/ps/IOPSKeys.h>
@@ -61,9 +62,14 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
NSMenuItem *notificationMenu = [[NSMenuItem alloc] initWithTitle:@"Notifications" action:nil keyEquivalent:@""];
[notificationMenu setSubmenu:notificationSubmenu];
+
+ // Updater menu
+ self.updaterMenu = [[NSMenuItem alloc] initWithTitle:@"Checking for updates…" action:nil keyEquivalent:@""];
+ [self.updaterMenu setEnabled:NO];
// Build the status menu
NSMenu *statusMenu = [[NSMenu alloc] initWithTitle:@"Status Menu"];
+ [statusMenu setDelegate:self];
#ifndef SANDBOX
[statusMenu addItem:self.startupToggle];
#endif
@@ -72,6 +78,8 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
[statusMenu addItemWithTitle:@"Energy Saver Preferences…" action:@selector(openEnergySaverPreference:) keyEquivalent:@""];
[statusMenu addItem:[NSMenuItem separatorItem]];
+ [statusMenu addItem:self.updaterMenu];
+ [statusMenu addItem:[NSMenuItem separatorItem]];
[statusMenu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@""];
// Create the status item and set initial text
@@ -236,6 +244,11 @@ - (void)openEnergySaverPreference:(id)sender
[[NSWorkspace sharedWorkspace] openFile:@"/System/Library/PreferencePanes/EnergySaver.prefPane"];
}
+- (void)openHomeUrl:(id)sender
+{
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://github.com/codler/Battery-Time-Remaining/downloads"]];
+}
+
#ifndef SANDBOX
- (void)toggleStartAtLogin:(id)sender
{
@@ -302,4 +315,35 @@ - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentN
return YES;
}
+- (void)menuWillOpen:(NSMenu *)menu
+{
+ // Stop checking if newer version is available
+ if ([self.updaterMenu isEnabled]) {
+ return;
+ }
+
+ // Check for newer version
+ [[HttpGet new] url:@"https://raw.github.com/codler/Battery-Time-Remaining/master/build_version" success:^(NSString *result){
+ NSInteger latestBuildVersion = [result integerValue];
+ NSInteger currentBuildVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] integerValue];
+
+ if (!latestBuildVersion) {
+ self.updaterMenu.title = @"Could not check for updates";
+ return;
+ }
+
+ // Newer version available
+ if (latestBuildVersion > currentBuildVersion) {
+ self.updaterMenu.title = @"A newer version is available";
+ [self.updaterMenu setAction:@selector(openHomeUrl:)];
+ [self.updaterMenu setEnabled:YES];
+ [self notify:@"A newer version is available"];
+ } else {
+ self.updaterMenu.title = @"Up to date";
+ }
+ } error:^(NSError *error) {
+ self.updaterMenu.title = @"Could not check for updates";
+ }];
+}
+
@end
@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>1.2.1</string>
+ <string>1.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>5</string>
+ <string>6</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
@@ -4,5 +4,7 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
+ <key>com.apple.security.network.client</key>
+ <true/>
</dict>
</plist>
@@ -0,0 +1,17 @@
+//
+// HttpGet.h
+// Battery Time Remaining
+//
+// Created by Han Lin Yap on 2012-08-12.
+// Copyright (c) 2012 Han Lin Yap. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface HttpGet : NSObject <NSURLConnectionDataDelegate, NSURLConnectionDelegate>
+
++ (HttpGet *)sharedHttpGet;
+- (void)url:(NSString *)url success:(void(^)(NSString *result))successBlock;
+- (void)url:(NSString *)url success:(void(^)(NSString *result))successBlock error:(void(^)(NSError *error))errorBlock;
+
+@end
@@ -0,0 +1,94 @@
+//
+// HttpGet.m
+// Battery Time Remaining
+//
+// Created by Han Lin Yap on 2012-08-12.
+// Copyright (c) 2012 Han Lin Yap. All rights reserved.
+//
+
+#import "HttpGet.h"
+
+static HttpGet *sHttpGet;
+
+// Private properties
+@interface HttpGet()
+@property (nonatomic, copy) void(^successBlock)(NSString *result);
+@property (nonatomic, copy) void(^errorBlock)(NSError *error);
+@property (nonatomic, strong) NSURLConnection *urlConnection;
+@property (nonatomic, strong) NSMutableData *tempData;
+@end
+
+@implementation HttpGet
+@synthesize successBlock, errorBlock, urlConnection, tempData;
+
+- (id)init
+{
+ self = [super init];
+ if (self) {
+ self.tempData = [NSMutableData new];
+ }
+ return self;
+}
+
+#pragma mark - Singleton setup
+
++ (void)initialize
+{
+ NSAssert(self == [HttpGet class],
+ @"HttpGet is not designed to be subclassed");
+ sHttpGet = [HttpGet new];
+}
+
++ (HttpGet *)sharedHttpGet
+{
+ return sHttpGet;
+}
+
+#pragma mark - Public methods
+
+- (void)url:(NSString *)url success:(void(^)(NSString *result))aSuccessBlock
+{
+ [self url:url success:aSuccessBlock error:nil];
+}
+
+- (void)url:(NSString *)url success:(void(^)(NSString *result))aSuccessBlock error:(void(^)(NSError *error))aErrorBlock
+{
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
+ self.successBlock = aSuccessBlock;
+ self.errorBlock = aErrorBlock;
+
+ // Cancel last url connection if we have one
+ [self.urlConnection cancel];
+
+ self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
+}
+
+#pragma mark - NSURLConnectionDelegate methods
+
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
+{
+ [self.tempData setLength:0];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
+{
+ [self.tempData appendData:data];
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
+{
+ self.errorBlock(error);
+
+ [self.tempData setLength:0];
+ self.urlConnection = nil;
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection
+{
+ self.successBlock([[NSString alloc] initWithData:self.tempData encoding:NSUTF8StringEncoding]);
+
+ [self.tempData setLength:0];
+ self.urlConnection = nil;
+}
+
+@end
View
@@ -0,0 +1 @@
+6

0 comments on commit 39573ee

Please sign in to comment.