From a92bf302c5237a0f110649b592e3f83f924d1c1e Mon Sep 17 00:00:00 2001 From: Paul Griffin Date: Mon, 21 Mar 2011 01:06:41 -0400 Subject: [PATCH] Updated for new API --- StatusBarItemClient.h => LSStatusBarClient.h | 6 +- LSStatusBarClient.mm | 265 ++++++++++++ LSStatusBarItem.h | 67 ++++ LSStatusBarItem.mm | 257 ++++++++++++ StatusBarItemServer.h => LSStatusBarServer.h | 16 +- LSStatusBarServer.mm | 356 +++++++++++++++++ Makefile | 5 +- README | 25 ++ StatusBarItemClient.mm | 170 -------- StatusBarItemServer.mm | 68 ---- Testing.mm | 88 ++++ UIStatusBarCustomItem.h | 4 + UIStatusBarCustomItem.mm | 58 ++- classlist.h | 3 + common.h | 4 + libstatusbar.mm | 399 +++++++++++-------- 16 files changed, 1376 insertions(+), 415 deletions(-) rename StatusBarItemClient.h => LSStatusBarClient.h (63%) create mode 100644 LSStatusBarClient.mm create mode 100644 LSStatusBarItem.h create mode 100644 LSStatusBarItem.mm rename StatusBarItemServer.h => LSStatusBarServer.h (50%) create mode 100644 LSStatusBarServer.mm create mode 100644 README delete mode 100644 StatusBarItemClient.mm delete mode 100644 StatusBarItemServer.mm create mode 100644 Testing.mm diff --git a/StatusBarItemClient.h b/LSStatusBarClient.h similarity index 63% rename from StatusBarItemClient.h rename to LSStatusBarClient.h index 6c90ed3..62922fd 100644 --- a/StatusBarItemClient.h +++ b/LSStatusBarClient.h @@ -1,18 +1,22 @@ -@interface StatusBarItemClient : NSObject +@interface LSStatusBarClient : NSObject { bool _isLocal; NSDictionary* _currentMessage; + NSArray* _titleStrings; } + (id) sharedInstance; - (id) init; +- (NSDictionary*) currentMessage; - (void) retrieveCurrentMessage; - (bool) processCurrentMessage; - (void) updateStatusBar; - (void) setProperties: (id) properties forItem: (NSString*) item; +- (NSString*) titleStringAtIndex: (int) idx; + @end \ No newline at end of file diff --git a/LSStatusBarClient.mm b/LSStatusBarClient.mm new file mode 100644 index 0000000..34c2590 --- /dev/null +++ b/LSStatusBarClient.mm @@ -0,0 +1,265 @@ + +//#define TESTING + +#import "common.h" +#import "defines.h" + +#import "classes.h" +#import "LSStatusBarClient.h" +#import "LSStatusBarServer.h" +#import "UIStatusBarCustomItem.h" + +void UpdateStatusBar(CFNotificationCenterRef center, LSStatusBarClient* client) +{ + [client updateStatusBar]; +} + +@implementation LSStatusBarClient + ++ (id) sharedInstance +{ + static LSStatusBarClient* client; + + if(!client) + { + client = [[self alloc] init]; + } + return client; +} + +- (id) init +{ + self = [super init]; + if(self) + { + _isLocal = $SpringBoard ? YES : NO; + + CFNotificationCenterRef darwin = CFNotificationCenterGetDarwinNotifyCenter(); + CFNotificationCenterAddObserver(darwin, self, (CFNotificationCallback) UpdateStatusBar, (CFStringRef) @"libstatusbar_changed", NULL, NULL); + + [self updateStatusBar]; + + } + return self; +} + +- (NSDictionary*) currentMessage +{ + return _currentMessage; +} + +- (void) retrieveCurrentMessage +{ + [_currentMessage release]; + if(_isLocal) + { + _currentMessage = [[[LSStatusBarServer sharedInstance] currentMessage] retain]; + } + else + { CPDistributedMessagingCenter* dmc = [CPDistributedMessagingCenter centerNamed: @"com.apple.springboard.libstatusbar"]; + + _currentMessage = [[dmc sendMessageAndReceiveReplyName: @"currentMessage" userInfo: nil] retain]; + } + NSDesc(_currentMessage); +} + +- (NSString*) titleStringAtIndex: (int) idx +{ + if(idx < (int)[_titleStrings count]) + { + return [_titleStrings objectAtIndex: idx]; + } + return nil; +} + +- (bool) processCurrentMessage +{ + bool ret = NO; + + //NSMutableDictionary *processedMessage = [_currentMessage mutableCopy]; + + NSMutableArray* processedKeys = [[_currentMessage objectForKey: @"keys"] mutableCopy]; + + + [_titleStrings release]; + _titleStrings = [[_currentMessage objectForKey: @"titleStrings"] retain]; + + + /* + if(_titleStrings) + { + [processedMessage removeObjectForKey: @"titleStrings"]; + } + */ + + int keyidx = 22; + + NSDesc(_currentMessage); + extern NSMutableArray* customItems[3]; + + for(int i=0; i<3; i++) + { + if(customItems[i]) + { + NSDesc(customItems[i]); + int cnt = [customItems[i] count]-1; + /* + extern NSMutableArray* allCustomItems; + if(allCustomItems) + { + int cnt = [allCustomItems count]-1; + */ + for(; cnt>= 0; cnt--) +// for(UIStatusBarCustomItem* item in customItems[i]) + { + UIStatusBarCustomItem* item = [customItems[i] objectAtIndex: cnt]; + //UIStatusBarCustomItem* item = [allCustomItems objectAtIndex: cnt]; + + NSString* indicatorName = [item indicatorName]; + + NSObject* properties = nil; + if(_currentMessage) + { + properties = [_currentMessage objectForKey: indicatorName]; + } + + if(!properties) + { + ret = YES; + + NSLog(@"removing item: %@", indicatorName); + [item removeAllViews]; + //[allCustomItems removeObjectAtIndex: cnt]; + [customItems[i] removeObjectAtIndex: cnt]; +// [customItems[i] removeObject: item]; + } + else + { + NSLog(@"keeping item: %@", indicatorName); + //[processedMessage removeObjectForKey: indicatorName]; + [processedKeys removeObject: indicatorName]; + + int &type(MSHookIvar(item, "_type")); + if(type > keyidx) + keyidx = type; + + item.properties = [properties isKindOfClass: [NSDictionary class]] ? (NSDictionary*) properties : nil; + } + } + } + else + { + NSLog(@"creating array"); + customItems[i] = [[NSMutableArray alloc] init]; + } + } + + keyidx++; + + if(processedKeys && [processedKeys count]) + { + ret = YES; + GETCLASS(UIStatusBarItem); + for(NSString* key in processedKeys)//processedMessage) + { + NSLog(@"adding item: %@", key); + + UIStatusBarCustomItem* item = [$UIStatusBarItem itemWithType: keyidx++]; + [item setIndicatorName: key]; + + NSObject* properties = [_currentMessage objectForKey: key]; + item.properties = [properties isKindOfClass: [NSDictionary class]] ? (NSDictionary*) properties : nil; + + + if([item leftOrder]) + { + if(!customItems[0]) + { + customItems[0] = [[NSMutableArray alloc] init]; + } + [customItems[0] addObject: item]; + } + else if([item rightOrder]) + { + if(!customItems[1]) + { + customItems[1] = [[NSMutableArray alloc] init]; + } + [customItems[1] addObject: item]; + } + else + { + if(!customItems[2]) + { + customItems[2] = [[NSMutableArray alloc] init]; + } + [customItems[2] addObject: item]; + } + } + } + + + //if(_titleStrings && [_titleStrings count]) + + // too many cases; just refresh the damn thing. + { + ret = YES; + } + + + + [processedKeys release]; + + NSLog(@"processCurrentMessage? %@", ret ? @"YES" : @"NO"); + return ret; +} + +- (void) updateStatusBar +{ + SelLog(); + + [self retrieveCurrentMessage]; + + // need a decent guard band because we do call before UIApp exists + if([self processCurrentMessage] && UIApp) + { + UIStatusBarForegroundView* _foregroundView = MSHookIvar([UIApp statusBar], "_foregroundView"); + if(_foregroundView) + { + //[_foregroundView _reflowItemViewsWithDuration: 0.0f suppressCenterAnimation: YES]; + + [_foregroundView setStatusBarData: (StatusBarData*) [$UIStatusBarServer getStatusBarData] actions: 1 animated: YES]; + } + } +} + +- (void) setProperties: (id) properties forItem: (NSString*) item +{ + SelLog(); + if(item) + { + NSString* bundleId = [[NSBundle mainBundle] bundleIdentifier]; + + if(_isLocal) + { + [[LSStatusBarServer sharedInstance] setProperties: properties forItem: item bundle: bundleId]; + } + else + { + + + NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys: + item, @"item", + properties, @"properties", + bundleId, @"bundle", + nil]; + + CPDistributedMessagingCenter* dmc = [CPDistributedMessagingCenter centerNamed: @"com.apple.springboard.libstatusbar"]; + + [dmc sendMessageName: @"setProperties:userInfo:" userInfo: dict]; + [dict release]; + } + } +} + +@end diff --git a/LSStatusBarItem.h b/LSStatusBarItem.h new file mode 100644 index 0000000..8f1eb98 --- /dev/null +++ b/LSStatusBarItem.h @@ -0,0 +1,67 @@ +enum StatusBarAlignment +{ + StatusBarAlignmentLeft = 1, + StatusBarAlignmentRight = 2, + StatusBarAlignmentCenter = 4 +}; + + +@interface LSStatusBarItem : NSObject +{ + NSString* _identifier; + NSMutableDictionary* _properties; + NSMutableSet* _delegates; + BOOL _manualUpdate; +} + ++ (void) updateItems; + +- (void) dealloc; +- (void) setProperties: (NSDictionary*) dict; + +@end + + +@interface LSStatusBarItem (API) + +- (id) initWithIdentifier: (NSString*) identifier alignment: (StatusBarAlignment) alignment; + +// bitmasks (e.g. left or right) are not supported yet +@property (nonatomic, readonly) StatusBarAlignment alignment; + +@property (nonatomic, getter=isVisible) BOOL visible; + +// useful only with left/right alignment - will throw error for center alignment +@property (nonatomic, assign) NSString* imageName; + +// useful only with center alignment - will throw error otherwise +// will not be visible on the lockscreen +@property (nonatomic, assign) NSString* titleString; + +// set to NO and manually call update if you need to make multiple changes +@property (nonatomic, getter=isManualUpdate) BOOL manualUpdate; + +// manually call if manualUpdate = YES +- (void) update; + +@end + + + + +@interface LSStatusBarItem (Unimplemented) + + +// leave alone unless you want to limit which apps your icon shows up in +@property (nonatomic, assign) NSString* exclusiveToApp; + +// convenience methods? +//@property (nonatomic, getter=isSpringBoardOnly) BOOL springBoardOnly; +//@property (getter=isCurrentAppOnly) BOOL currentAppOnly; + +// delegate must respond to @selector(statusBarAction:); only valid from inside of SpringBoard +- (void) addTouchDelegate: (id) delegate; +- (void) removeTouchDelegate: (id) delegate; + + +@end diff --git a/LSStatusBarItem.mm b/LSStatusBarItem.mm new file mode 100644 index 0000000..2101c86 --- /dev/null +++ b/LSStatusBarItem.mm @@ -0,0 +1,257 @@ + + +//#define TESTING + +#import "common.h" +#import "defines.h" + +#import "LSStatusBarItem.h" +#import "LSStatusBarClient.h" + + +NSMutableDictionary* sbitems = nil; + +@implementation LSStatusBarItem + + ++ (void) updateItems +{ + NSDictionary* currentMessage = [[LSStatusBarClient sharedInstance] currentMessage]; + + if(!sbitems) + { + sbitems = [NSMutableDictionary new]; + } + + for(NSString* key in sbitems) + { + NSDictionary* dict = [currentMessage objectForKey: key]; + + if(dict) + { + NSArray* idArray = [sbitems objectForKey: key]; + for(LSStatusBarItem* item in idArray) + { + [item setProperties: dict]; + } + } + } +} + + +- (id) initWithIdentifier: (NSString*) identifier alignment: (StatusBarAlignment) alignment +{ + if(!identifier) + { + [NSException raise: NSInternalInconsistencyException format: @"LSStatusBarItem: No identifer specified"]; + } + + if(!alignment) + { + [NSException raise: NSInternalInconsistencyException format: @"LSStatusBarItem: Alignment not specified"]; + } + + if(!UIApp) + { + [NSException raise: NSInternalInconsistencyException format: @"LSStatusBarItem: Wait for UIApp to load!"]; + } + + + if((self = [super init])) + { + // get the current message + NSDictionary* currentMessage; + { + LSStatusBarClient* client = [LSStatusBarClient sharedInstance]; + + currentMessage = [client currentMessage]; + if(!currentMessage) + [client retrieveCurrentMessage]; + + if(!currentMessage) + { + [NSException raise: NSInternalInconsistencyException format: @"LSStatusBarItem: Cannot retrieve the current message!"]; + } + + } + + // save all the settings + { + _identifier = [identifier retain]; + + [self setProperties: [currentMessage objectForKey: _identifier]]; + + NSNumber* align = [_properties objectForKey: @"alignment"]; + if(!align) + { + [_properties setObject: [NSNumber numberWithInt: alignment] forKey: @"alignment"]; + } + else if([align intValue] != alignment) + { + [NSException raise: NSInternalInconsistencyException format: @"LSStatusBarItem: You cannot specify a new alignment!"]; + } + } + + // keep track of StatusBarItem(s) + { + if(!sbitems) + { + sbitems = [NSMutableDictionary new]; + } + + NSMutableArray* idArray = [sbitems objectForKey: identifier]; + if(!idArray) + { + // this creates a retain/release-less NSMutableArray + idArray = (NSMutableArray*) CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); + [sbitems setObject: idArray forKey: identifier]; + CFRelease(idArray); + } + + if(idArray) + { + [idArray addObject: self]; + } + } + return self; + } + + return nil; +} + + +- (void) dealloc +{ + if(sbitems) + { + NSMutableArray* idArray = [sbitems objectForKey: _identifier]; + + // kill the current item count + if(idArray) + { + [idArray removeObject: self]; + + if([idArray count] == 0) + { + // item is no longer in use by this process, let the server no + [[LSStatusBarClient sharedInstance] setProperties: nil forItem: _identifier]; + } + } + + [_identifier release]; + [_properties release]; + } + + [super dealloc]; +} + + +- (void) setProperties: (NSDictionary*) dict +{ + if(!dict) + { + _properties = [NSMutableDictionary new]; + } + else + { + _properties = [dict mutableCopy]; + } +} + + +- (StatusBarAlignment) alignment +{ + NSNumber* alignment = [_properties objectForKey: @"alignment"]; + return alignment ? (StatusBarAlignment) [alignment intValue] : StatusBarAlignmentLeft; +} + +- (void) setVisible: (BOOL) visible +{ + [_properties setObject: [NSNumber numberWithBool: visible] forKey: @"visible"]; + + if(!_manualUpdate) + [self update]; +} + +- (BOOL) isVisible +{ + NSNumber* visible = [_properties objectForKey: @"visible"]; + return visible ? [visible boolValue] : YES; +} + + +- (void) setImageName: (NSString*) imageName +{ + if(self.alignment & StatusBarAlignmentCenter) + { + [NSException raise: NSInternalInconsistencyException format: @"LSStatusBarItem: Cannot use images with a center alignment"]; + } + [_properties setObject: imageName forKey: @"imageName"]; + + if(!_manualUpdate) + [self update]; +} + +- (NSString*) imageName +{ + return [_properties objectForKey: @"imageName"]; +} + +- (void) setTitleString: (NSString*) string +{ + if(self.alignment & (StatusBarAlignmentLeft | StatusBarAlignmentRight)) + { + [NSException raise: NSInternalInconsistencyException format: @"LSStatusBarItem: Cannot use a title string with a side alignment"]; + } + + [_properties setObject: string forKey: @"titleString"]; + + if(!_manualUpdate) + [self update]; +} + +- (NSString*) titleString +{ + return [_properties objectForKey: @"titleString"]; +} + + +- (void) setManualUpdate: (BOOL) manualUpdate +{ + _manualUpdate = manualUpdate; +} + +- (BOOL) isManualUpdate +{ + return _manualUpdate; +} + +- (void) update +{ + SelLog(); + [[LSStatusBarClient sharedInstance] setProperties: _properties forItem: _identifier]; +} + + +// future API + +- (void) setExclusiveToApp: (NSString*) bundleId +{ + [_properties setObject: bundleId forKey: @"exclusiveToApp"]; +} + +- (NSString*) exclusiveToApp +{ + return [_properties objectForKey: @"exclusiveToApp"]; +} + +- (void) addTouchDelegate: (id) delegate +{ +} + +- (void) removeTouchDelegate: (id) delegate +{ +} + + +@end \ No newline at end of file diff --git a/StatusBarItemServer.h b/LSStatusBarServer.h similarity index 50% rename from StatusBarItemServer.h rename to LSStatusBarServer.h index 24b2694..c54b4b5 100644 --- a/StatusBarItemServer.h +++ b/LSStatusBarServer.h @@ -1,17 +1,29 @@ -@interface StatusBarItemServer : NSObject +@interface LSStatusBarServer : NSObject { CPDistributedMessagingCenter *_dmc; NSMutableDictionary* _currentMessage; + NSMutableArray* _currentKeys; + NSMutableDictionary* _currentKeyUsage; + + CFRunLoopTimerRef timer; } + (id) sharedInstance; - (id) init; +- (void) appDidExit: (NSString*) bundle; + - (void) setProperties: (NSString*) message userInfo: (NSDictionary*) userInfo; -- (void) setProperties: (id) properties forItem: (NSString*) item; +- (void) setProperties: (id) properties forItem: (NSString*) item bundle: (NSString*) bundle; - (NSMutableDictionary*) currentMessage; +- (void) incrementTimer; +- (void) updateLockStatus; + +- (void) startTimer; +- (void) stopTimer; + @end \ No newline at end of file diff --git a/LSStatusBarServer.mm b/LSStatusBarServer.mm new file mode 100644 index 0000000..f94285e --- /dev/null +++ b/LSStatusBarServer.mm @@ -0,0 +1,356 @@ + +//#define TESTING + +#import "common.h" +#import "defines.h" + +#import "classes.h" +#import "LSStatusBarServer.h" + +#import "LSStatusBarItem.h" + + +void updateLockStatus(CFNotificationCenterRef center, LSStatusBarServer* server) +{ + NSLine(); + [server updateLockStatus]; +} + +void incrementTimer()//CFRunLoopTimerRef timer, LSStatusBarServer* self) +{ + [[LSStatusBarServer sharedInstance] incrementTimer]; +} + + +@implementation LSStatusBarServer + + ++ (id) sharedInstance +{ + static LSStatusBarServer* server; + + if(!server) + { + server = [[self alloc] init]; + } + return server; +} + + +- (id) init +{ + self = [super init]; + if(self) + { + _dmc = [CPDistributedMessagingCenter centerNamed: @"com.apple.springboard.libstatusbar"]; + [_dmc runServerOnCurrentThread]; + [_dmc registerForMessageName: @"currentMessage" target: self selector: @selector(currentMessage)]; + [_dmc registerForMessageName: @"setProperties:userInfo:" target: self selector: @selector(setProperties:userInfo:)]; + + _currentMessage = [[NSMutableDictionary alloc] init]; + _currentKeys = [[NSMutableArray alloc] init]; + _currentKeyUsage = [[NSMutableDictionary alloc] init]; + + CFNotificationCenterRef darwin = CFNotificationCenterGetDarwinNotifyCenter(); + CFNotificationCenterAddObserver(darwin, self, (CFNotificationCallback) updateLockStatus, (CFStringRef) @"com.apple.springboard.lockstate", self, NULL); + + } + return self; +} + + + + +- (NSMutableDictionary*) currentMessage +{ + return _currentMessage; +} + + +- (void) processMessageCommon +{ + NSMutableArray* titleStrings = [NSMutableArray array]; + for(NSString* key in _currentKeys) + { + NSDictionary* dict = [_currentMessage objectForKey: key]; + + if(!dict || ![dict isKindOfClass: [NSDictionary class]]) + continue; + + NSNumber* alignment = [dict objectForKey: @"alignment"]; + if(alignment && ((StatusBarAlignment) [alignment intValue]) == StatusBarAlignmentCenter) + { + NSLine(); + NSNumber* visible = [dict objectForKey: @"visible"]; + if(!visible || [visible boolValue]) + { + NSLine(); + if(NSString* titleString = [dict objectForKey: @"titleString"]) + { + NSLine(); + [titleStrings addObject: titleString]; + } + } + } + } + + [_currentMessage setValue: _currentKeys forKey: @"keys"]; + NSDesc(_currentKeys); + + + + if([titleStrings count]) + { + [_currentMessage setValue: titleStrings forKey: @"titleStrings"]; + + [self startTimer]; + } + else + { + [_currentMessage setValue: nil forKey: @"titleStrings"]; + + //if(!timer) + // notify_post("libstatusbar_changed"); + [self stopTimer]; + } + NSDesc(_currentMessage); + + notify_post("libstatusbar_changed"); +} + +- (void) setProperties: (id) properties forItem: (NSString*) item bundle: (NSString*) bundle +{ + SelLog(); + if(!item || !bundle) + { + NSLog(@"missing info. returning..."); + return; + } + + // get the current item usage by bundles + NSMutableArray* bundles = [_currentKeyUsage objectForKey: item]; + if(!bundles) + { + bundles = [NSMutableArray array]; + [_currentKeyUsage setObject: bundles forKey: item]; + } + + int itemIdx = [_currentKeys indexOfObject: item]; + + if(properties) + { + [_currentMessage setValue: properties forKey: item]; + + if(![bundles containsObject: bundle]) + { + [bundles addObject: bundle]; + } + + if(itemIdx == NSNotFound) + { + [_currentKeys addObject: item]; + } + } + else + { + [bundles removeObject: bundle]; + NSLog(@"removing object"); + + if([bundles count]==0) + { + // object is truly dead + [_currentMessage setValue: nil forKey: item]; + + if(itemIdx!=NSNotFound) + [_currentKeys removeObjectAtIndex: itemIdx]; + } + } + + /* + int itemIdx = [_currentKeys indexOfObject: item]; + if(!properties && itemIdx != NSNotFound) + { + [_currentKeys removeObjectAtIndex: itemIdx]; + } + else if(properties && itemIdx == NSNotFound) + { + [_currentKeys addObject: item]; + } + */ + + + // find all title strings + + [self processMessageCommon]; +} + + +- (void) appDidExit: (NSString*) bundle +{ + + + int nKeys = [_currentKeys count]; + for(int i=nKeys - 1; i>=0; i--) + { + NSString* item = [_currentKeys objectAtIndex: i]; + + NSMutableArray* bundles = [_currentKeyUsage objectForKey: item]; + if(!bundles) + { + continue; + } + + if([bundles containsObject: bundle]) + { + [bundles removeObject: bundle]; + NSLog(@"removing object"); + + if([bundles count]==0) + { + // object is truly dead + [_currentMessage setValue: nil forKey: item]; + + int itemIdx = [_currentKeys indexOfObject: item]; + if(itemIdx!=NSNotFound) + [_currentKeys removeObjectAtIndex: itemIdx]; + } + } + } + + [self processMessageCommon]; +} + + + + +- (void) setProperties: (NSString*) message userInfo: (NSDictionary*) userInfo +{ + NSString* item = [userInfo objectForKey: @"item"]; + NSDictionary* properties = [userInfo objectForKey: @"properties"]; + NSString* bundleId = [userInfo objectForKey: @"bundle"]; + + [self setProperties: properties forItem: item bundle: bundleId]; +} + +- (void) startTimer +{ + NSLine(); + + // is timer already running? + if(timer) + { + NSLog(@"timer is already active. Leaving it alone"); + return; + } + + // check lock status + uint64_t locked; + { + int token = 0; + notify_register_check("com.apple.springboard.lockstate", &token); + notify_get_state(token, &locked); + } + + NSLine(); + + // reset timer state + [self stopTimer]; + NSLine(); + + if(!locked) + { + NSLine(); + + NSArray* titleStrings = [_currentMessage objectForKey: @"titleStrings"]; + if(titleStrings && [titleStrings count]) + { + NSLine(); + + timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+3.5f, 3.5f, 0, 0, (CFRunLoopTimerCallBack) incrementTimer, NULL); + CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes); + } + } + NSLine(); + +} + +- (void) stopTimer +{ + NSLine(); + + // reset the statusbar state + { + const char* notif = "libstatusbar_changed"; + + uint64_t value = NSNotFound; + int token = 0; + notify_register_check(notif, &token); + + notify_set_state(token, value); + + if(timer) // only post a notification if the timer was running + notify_post(notif); + } + + // kill timer + if(timer) + { + CFRunLoopTimerInvalidate(timer); + CFRelease(timer); + timer = nil; + } + NSLine(); +} + + +- (void) incrementTimer +{ + NSLine(); + + NSArray* titleStrings = [_currentMessage objectForKey: @"titleStrings"]; + + const char* notif = "libstatusbar_changed"; + + if(titleStrings && [titleStrings count]) + { + uint64_t value; + int token = 0; + notify_register_check(notif, &token); + notify_get_state(token, &value); + + value++; + if(value > [titleStrings count]) + { + value = 0; + } + + NSLog(@"idx = %ld", value); + + notify_set_state(token, value); + notify_post(notif); + } + else + { + [self stopTimer]; + /* + NSLog(@"idx = %ld", value); + + CFRunLoopTimerInvalidate(timer); + CFRelease(timer); + timer = nil; + + notify_post(notif); + */ + } +} + + +- (void) updateLockStatus +{ + [self stopTimer]; + [self startTimer]; +} + + + +@end \ No newline at end of file diff --git a/Makefile b/Makefile index c64229a..9a04be7 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ TWEAK_NAME = libstatusbar libstatusbar_OBJCC_FILES = libstatusbar.mm Classes.mm \ - StatusBarItemClient.mm StatusBarItemServer.mm \ - UIStatusBarCustomItem.mm UIStatusBarCustomItemView.mm + LSStatusBarClient.mm LSStatusBarServer.mm \ + UIStatusBarCustomItem.mm UIStatusBarCustomItemView.mm \ + LSStatusBarItem.mm # Testing.mm libstatusbar_FRAMEWORKS = UIKit libstatusbar_PRIVATE_FRAMEWORKS = AppSupport SpringboardServices diff --git a/README b/README new file mode 100644 index 0000000..45cf42c --- /dev/null +++ b/README @@ -0,0 +1,25 @@ +Libstatusbar: pwning the statusbar for you. + +Libstatusbar is chiefly a compatibility library for 3.x-like statusbar icon support under 4.x, but allows for a more advanced API for custom time strings and icon swapping. + + +All icons should be placed in either: + +3.x style, does not support retina icons: +Silver: /System/Library/CoreServices/SpringBoard/Default_.png +Black: /System/Library/CoreServices/SpringBoard/FSO_.png + +4.x style, supports retina (@2x) icons: +Silver: /System/Library/Frameworks/UIKit.framework/Silver_.png +Black: /System/Library/Frameworks/UIKit.framework/Black_.png + +--- BASIC API --- + +3.x-style, simply add/remove items. These will appear in the right list. + +[UIApp addStatusBarImageNamed: (NSString*) name]; +[UIApp removeStatusBarImageNamed: (NSString*) name]; + +--- ADVANCED API --- + +See LSStatusBarItem.h for the API and Testing.mm for example usage diff --git a/StatusBarItemClient.mm b/StatusBarItemClient.mm deleted file mode 100644 index 750c93f..0000000 --- a/StatusBarItemClient.mm +++ /dev/null @@ -1,170 +0,0 @@ - -//#define TESTING - -#import "common.h" -#import "defines.h" - -#import "classes.h" -#import "StatusBarItemClient.h" -#import "StatusBarItemServer.h" -#import "UIStatusBarCustomItem.h" - -void UpdateStatusBar(CFNotificationCenterRef center, StatusBarItemClient* client) -{ - SelLog(); - [client updateStatusBar]; -} - -@implementation StatusBarItemClient - -StatusBarItemClient* sharedItemClient; -+ (id) sharedInstance -{ - if(!sharedItemClient) - { - sharedItemClient = [[self alloc] init]; - } - return sharedItemClient; -} - -- (id) init -{ - self = [super init]; - if(self) - { - _isLocal = $SpringBoard ? YES : NO; - - CFNotificationCenterRef darwin = CFNotificationCenterGetDarwinNotifyCenter(); - CFNotificationCenterAddObserver(darwin, self, (CFNotificationCallback) UpdateStatusBar, (CFStringRef) @"libstatusbar_changed", NULL, NULL); - - [self updateStatusBar]; - - } - return self; -} - - -- (void) retrieveCurrentMessage -{ - [_currentMessage release]; - if(_isLocal) - { - _currentMessage = [[[StatusBarItemServer sharedInstance] currentMessage] retain]; - } - else - { CPDistributedMessagingCenter* dmc = [CPDistributedMessagingCenter centerNamed: @"com.apple.springboard.libstatusbar"]; - - _currentMessage = [[dmc sendMessageAndReceiveReplyName: @"currentMessage" userInfo: nil] retain]; - } - NSDesc(_currentMessage); -} - -- (bool) processCurrentMessage -{ - bool ret = NO; - - NSMutableDictionary *processedMessage = [_currentMessage mutableCopy]; - - int keyidx = 22; - -// NSDesc(processedMessage); - - for(int i=0; i<3; i++) - { - extern NSMutableArray* customItems[3]; - if(customItems[i]) - { - NSDesc(customItems[i]); - int cnt = [customItems[i] count]-1; - for(; cnt>= 0; cnt--) -// for(UIStatusBarCustomItem* item in customItems[i]) - { - UIStatusBarCustomItem* item = [customItems[i] objectAtIndex: cnt]; - - NSString* indicatorName = [item indicatorName]; - if(processedMessage==nil || [processedMessage objectForKey: indicatorName] == nil) - { - ret = YES; - - NSLog(@"removing item: %@", indicatorName); - [item removeAllViews]; - [customItems[i] removeObjectAtIndex: cnt]; -// [customItems[i] removeObject: item]; - } - else - { - NSLog(@"keeping item: %@", indicatorName); - [processedMessage removeObjectForKey: indicatorName]; - int &type(MSHookIvar(item, "_type")); - if(type > keyidx) - keyidx = type; - - } - } - } - else - { - NSLog(@"creating array"); - - customItems[i] = [[NSMutableArray alloc] init]; - } - } - - keyidx++; - - if(processedMessage && [processedMessage count]) - { - ret = YES; - GETCLASS(UIStatusBarItem); - for(NSString* key in processedMessage) - { - NSLog(@"adding item: %@", key); - [[$UIStatusBarItem itemWithType: keyidx++] setIndicatorName: key]; - } - } - NSLog(@"processCurrentMessage? %@", ret ? @"YES" : @"NO"); - return ret; -} - -- (void) updateStatusBar -{ - SelLog(); - - [self retrieveCurrentMessage]; - - // need a decent guard band because we do call before UIApp exists - if([self processCurrentMessage] && UIApp) - { - UIStatusBarForegroundView* _foregroundView = MSHookIvar([UIApp statusBar], "_foregroundView"); - if(_foregroundView) - { - [_foregroundView setStatusBarData: (StatusBarData*) [$UIStatusBarServer getStatusBarData] actions: 0 animated: YES]; - } - } -} - -- (void) setProperties: (id) properties forItem: (NSString*) item -{ - SelLog(); - if(item) - { - if(_isLocal) - { - [[StatusBarItemServer sharedInstance] setProperties: properties forItem: item]; - } - else - { - NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys: - item, @"item", - properties, @"properties", - nil]; - - CPDistributedMessagingCenter* dmc = [CPDistributedMessagingCenter centerNamed: @"com.apple.springboard.libstatusbar"]; - - [dmc sendMessageName: @"setProperties:userInfo:" userInfo: dict]; - [dict release]; - } - } -} - -@end diff --git a/StatusBarItemServer.mm b/StatusBarItemServer.mm deleted file mode 100644 index 210e8cb..0000000 --- a/StatusBarItemServer.mm +++ /dev/null @@ -1,68 +0,0 @@ - -#import "common.h" -#import "defines.h" - -#import "classes.h" -//#import "StatusBarItemClient.h" -#import "StatusBarItemServer.h" -//#import "UIStatusBarCustomItem.h" - -@implementation StatusBarItemServer - -StatusBarItemServer* sharedItemServer; -+ (id) sharedInstance -{ - if(!sharedItemServer) - { - sharedItemServer = [[self alloc] init]; - } - return sharedItemServer; -} - - -- (id) init -{ - self = [super init]; - if(self) - { - _dmc = [CPDistributedMessagingCenter centerNamed: @"com.apple.springboard.libstatusbar"]; - [_dmc runServerOnCurrentThread]; - [_dmc registerForMessageName: @"currentMessage" target: self selector: @selector(currentMessage)]; - [_dmc registerForMessageName: @"setProperties:userInfo:" target: self selector: @selector(setProperties:userInfo:)]; - - _currentMessage = [[NSMutableDictionary alloc] init]; - - - } - return self; -} - -- (NSMutableDictionary*) currentMessage -{ - return _currentMessage; -} - -- (void) setProperties: (id) properties forItem: (NSString*) item -{ - SelLog(); - if(item) - { - // use setValue instead of setObject - [_currentMessage setValue: properties forKey: item]; - } - NSDesc(_currentMessage); - notify_post("libstatusbar_changed"); - -} - -- (void) setProperties: (NSString*) message userInfo: (NSDictionary*) userInfo -{ - NSString* item = [userInfo objectForKey: @"item"]; - NSDictionary* properties = [userInfo objectForKey: @"properties"]; - - [self setProperties: properties forItem: item]; - - -} - -@end \ No newline at end of file diff --git a/Testing.mm b/Testing.mm new file mode 100644 index 0000000..73a9821 --- /dev/null +++ b/Testing.mm @@ -0,0 +1,88 @@ + +#import "common.h" +#import "defines.h" + +#import "classes.h" + +#import "LSStatusBarItem.h" + + +LSStatusBarItem* playItem; + +void addPlay() +{ + playItem = [[LSStatusBarItem alloc] initWithIdentifier: @"libstatusbar.Play" alignment: StatusBarAlignmentLeft]; + playItem.imageName = @"Play"; +// [UIApp addStatusBarImageNamed: @"Bluetooth"]; +} + +void playToBT() +{ + playItem.imageName = @"Bluetooth"; +} + +LSStatusBarItem* centerItem; + +LSStatusBarItem* centerItem2; + + +void addCenterText() +{ + centerItem = [[LSStatusBarItem alloc] initWithIdentifier: @"libstatusbar.Center" alignment: StatusBarAlignmentCenter]; + centerItem.titleString = @"Test string"; + + centerItem2 = [[LSStatusBarItem alloc] initWithIdentifier: @"libstatusbar.Center2" alignment: StatusBarAlignmentCenter]; + centerItem2.titleString = @"is a reallly long test string"; +} + +void modifyCenterText() +{ + [playItem release]; + + centerItem2.titleString = @"is an even longer test string to fill the screen"; +} + + +void DelayedTesting() +{ + { + { + float delay = 4.0f; + CFRunLoopTimerCallBack callback = (CFRunLoopTimerCallBack) addPlay; + + CFRunLoopTimerRef waitTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+delay, 0.0f, 0, 0, callback, NULL); + CFRunLoopAddTimer(CFRunLoopGetMain(), waitTimer, kCFRunLoopCommonModes); + } + { + float delay = 16.0f; + CFRunLoopTimerCallBack callback = (CFRunLoopTimerCallBack) playToBT; + + CFRunLoopTimerRef waitTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+delay, 0.0f, 0, 0, callback, NULL); + CFRunLoopAddTimer(CFRunLoopGetMain(), waitTimer, kCFRunLoopCommonModes); + } + { + float delay = 2.0f; + CFRunLoopTimerCallBack callback = (CFRunLoopTimerCallBack) addCenterText; + + CFRunLoopTimerRef waitTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+delay, 0.0f, 0, 0, callback, NULL); + CFRunLoopAddTimer(CFRunLoopGetMain(), waitTimer, kCFRunLoopCommonModes); + } + + { + float delay = 30.0f; + CFRunLoopTimerCallBack callback = (CFRunLoopTimerCallBack) modifyCenterText; + + CFRunLoopTimerRef waitTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+delay, 0.0f, 0, 0, callback, NULL); + CFRunLoopAddTimer(CFRunLoopGetMain(), waitTimer, kCFRunLoopCommonModes); + } + } +} + +__attribute__((constructor)) void TestingStart() +{ + GETCLASS(MobileSafariWindow); + if($MobileSafariWindow) + { + DelayedTesting(); + } +} \ No newline at end of file diff --git a/UIStatusBarCustomItem.h b/UIStatusBarCustomItem.h index a5eda54..44a1770 100644 --- a/UIStatusBarCustomItem.h +++ b/UIStatusBarCustomItem.h @@ -12,6 +12,10 @@ - (void) setIndicatorName: (NSString*) name; +//@property (nonatomic, retain) NSString* indicatorName; +@property (nonatomic, retain) NSDictionary* properties; + + @end void ClassCreate_UIStatusBarCustomItem(); \ No newline at end of file diff --git a/UIStatusBarCustomItem.mm b/UIStatusBarCustomItem.mm index 011bb05..1ab9022 100644 --- a/UIStatusBarCustomItem.mm +++ b/UIStatusBarCustomItem.mm @@ -5,6 +5,8 @@ #import "classes.h" #import "UIStatusBarCustomItem.h" +#import "LSStatusBarItem.h" + //Class $UIStatusBarCustomItem; int UIStatusBarCustomItem$type(UIStatusBarCustomItem* self, SEL sel) @@ -14,6 +16,15 @@ int UIStatusBarCustomItem$leftOrder(UIStatusBarCustomItem* self, SEL sel) { + if(NSDictionary* properties = [self properties]) + { + NSNumber* nsalign = [properties objectForKey: @"alignment"]; + StatusBarAlignment alignment = nsalign ? (StatusBarAlignment) [nsalign intValue] : StatusBarAlignmentRight; + if(alignment & StatusBarAlignmentLeft) + { + return 15; + } + } return 0; } @@ -26,7 +37,23 @@ int UIStatusBarCustomItem$rightOrder(UIStatusBarCustomItem* self, SEL sel) { - return 15; + if(NSDictionary* properties = [self properties]) + { + NSNumber* nsalign = [properties objectForKey: @"alignment"]; + StatusBarAlignment alignment = nsalign ? (StatusBarAlignment) [nsalign intValue] : StatusBarAlignmentRight; + if(alignment & StatusBarAlignmentRight) + { + return 15; + } + else + { + return 0; + } + } + else + { + return 15; + } } int UIStatusBarCustomItem$priority(UIStatusBarCustomItem* self, SEL sel) @@ -41,6 +68,13 @@ NSString* UIStatusBarCustomItem$indicatorName(UIStatusBarCustomItem* self, SEL sel) { + if(NSDictionary* properties = [self properties]) + { + NSString* name = [properties objectForKey: @"imageName"]; + if(name) + return name; + } + NSString* &_indicatorName(MSHookIvar(self, "_indicatorName")); return _indicatorName; } @@ -53,6 +87,21 @@ } + +NSDictionary* UIStatusBarCustomItem$properties(UIStatusBarCustomItem* self, SEL sel) +{ + NSDictionary* &_properties(MSHookIvar(self, "_properties")); + return _properties; +} + +void UIStatusBarCustomItem$setProperties$(UIStatusBarCustomItem* self, SEL sel, NSDictionary* properties) +{ + NSDictionary* &_properties(MSHookIvar(self, "_properties")); + [_properties release]; + _properties = [properties retain]; +} + + UIStatusBarItemView* UIStatusBarCustomItem$viewForManager$(id self, SEL sel, id manager) { CFMutableDictionaryRef &_views(MSHookIvar(self, "_views")); @@ -110,8 +159,15 @@ void ClassCreate_UIStatusBarCustomItem() class_addMethod($UIStatusBarCustomItem, @selector(removeAllViews), (IMP) UIStatusBarCustomItem$removeAllViews, "v@:"); + + class_addIvar($UIStatusBarCustomItem, "_properties", sizeof(id), 0x4, "@"); + class_addIvar($UIStatusBarCustomItem, "_indicatorName", sizeof(id), 0x4, "@"); class_addMethod($UIStatusBarCustomItem, @selector(indicatorName), (IMP) UIStatusBarCustomItem$indicatorName, "@@:"); class_addMethod($UIStatusBarCustomItem, @selector(setIndicatorName:), (IMP) UIStatusBarCustomItem$setIndicatorName$, "v@:@"); + + class_addMethod($UIStatusBarCustomItem, @selector(properties), (IMP) UIStatusBarCustomItem$properties, "@@:"); + class_addMethod($UIStatusBarCustomItem, @selector(setProperties:), (IMP) UIStatusBarCustomItem$setProperties$, "v@:@"); + objc_registerClassPair($UIStatusBarCustomItem); } diff --git a/classlist.h b/classlist.h index b520a13..bd4b809 100644 --- a/classlist.h +++ b/classlist.h @@ -9,3 +9,6 @@ CLCLASS(SpringBoard); CLCLASS(UIStatusBarCustomItemView); CLCLASS(UIStatusBarCustomItem); + +CLCLASS(UIStatusBarItemView); +CLCLASS(UIStatusBarTimeItemView); \ No newline at end of file diff --git a/common.h b/common.h index f27c7e9..86d47ee 100644 --- a/common.h +++ b/common.h @@ -23,8 +23,12 @@ extern "C" int SBSSpringBoardServerPort(); #import "UIStatusBarForegroundView.h" #import "UIStatusBarServer.h" +#import "UIStatusBar.h" +#import "UIStatusBarTimeItemView.h" + #import "CPDistributedMessagingCenter.h" +// structures listed here are NOT valid for iOS 4.2+ - at least two more "items" exist /* struct StatusBarData { diff --git a/libstatusbar.mm b/libstatusbar.mm index 50a5534..1eefff1 100644 --- a/libstatusbar.mm +++ b/libstatusbar.mm @@ -7,67 +7,34 @@ #import "classes.h" #import "UIStatusBarCustomItem.h" #import "UIStatusBarCustomItemView.h" -#import "StatusBarItemServer.h" -#import "StatusBarItemClient.h" +#import "LSStatusBarServer.h" +#import "LSStatusBarClient.h" + + +#import "LSStatusBarItem.h" NSMutableArray* customItems[3]; // left, right, center -// called by -// (void) [UIStatusBarLayoutManager* prepareEnabledItems: (BOOL[20]) itemIsEnabled]; -// (void) [UIStatusBarForegroundView* _reflowItemViewsWithDuration: (double) dur suppressCenterAnimation: (BOOL) suppress]; +#pragma mark UIStatusBar* Hooks + HOOKDEF(id, UIStatusBarItem, itemWithType$, int type) { -// HookLog(); - id ret = CALL_ORIG(UIStatusBarItem, itemWithType$, type); + + // construct our own custom item if(ret==nil) { - NSLog(@"type = %d", type); ret = [[$UIStatusBarCustomItem alloc] initWithType: type]; - if([ret leftOrder]) - { - if(!customItems[0]) - { - customItems[0] = [[NSMutableArray alloc] init]; - } - [customItems[0] addObject: ret]; - } - else if([ret rightOrder]) - { - if(!customItems[1]) - { - customItems[1] = [[NSMutableArray alloc] init]; - } - [customItems[1] addObject: ret]; - } - else - { - if(!customItems[2]) - { - customItems[2] = [[NSMutableArray alloc] init]; - } - [customItems[2] addObject: ret]; - } - NSType(ret); - - // now let's get to work } return ret; } -// May return NO if a callback function (designated separately) exists + refuses to show -// called by -// (void) [UIStatusBarForegroundView setStatusBarData: (StatusBarData*) data actions: (int) actions animated: (BOOL) animated]; -/* -HOOKDEF(BOOL, UIStatusBarItem, itemType$canBeEnabledForData$, int type, StatusBarData* data) -{ -// HookLog(); - BOOL ret = CALL_ORIG(UIStatusBarItem, itemType$canBeEnabledForData$, type, data); - return ret; -} -*/ + +@interface UIStatusBarItemView (extraSelector) ++ (UIStatusBarItemView*) createViewForItem: (UIStatusBarItem*) item withData: (void*) data actions: (int) actions foregroundStyle: (int) style; +@end UIStatusBarItemView* InitializeView(UIStatusBarLayoutManager* self, id item) { @@ -78,37 +45,42 @@ return _view; } - GETCLASS(UIStatusBarItemView); - GETVAR(UIStatusBarForegroundView*, _foregroundView); int foregroundStyle = [_foregroundView foregroundStyle]; - NSLog(@"foregroundStyle = %d", foregroundStyle); - _view = [$UIStatusBarItemView createViewForItem: item foregroundStyle: foregroundStyle]; - [_view setLayoutManager: self]; - - GETVAR(int, _region); - switch(_region) + if([$UIStatusBarItemView respondsToSelector: @selector(createViewForItem:foregroundStyle:)]) { - case 0: - { - [_view setContentMode: UIViewContentModeLeft]; - [_view setAutoresizingMask: UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin]; - break; + _view = [$UIStatusBarItemView createViewForItem: item foregroundStyle: foregroundStyle]; } - case 1: + else if([$UIStatusBarItemView respondsToSelector: @selector(createViewForItem:withData:actions:foregroundStyle:)]) { - [_view setContentMode: UIViewContentModeRight]; - [_view setAutoresizingMask: UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin]; - break; + _view = [$UIStatusBarItemView createViewForItem: item withData: nil actions: 0 foregroundStyle: foregroundStyle]; } - case 2: + + [_view setLayoutManager: self]; + + GETVAR(int, _region); + switch(_region) { - [_view setContentMode: UIViewContentModeLeft]; - [_view setAutoresizingMask: UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin]; // 0x25 - break; - } + case 0: + { + [_view setContentMode: UIViewContentModeLeft]; + [_view setAutoresizingMask: UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin]; + break; + } + case 1: + { + [_view setContentMode: UIViewContentModeRight]; + [_view setAutoresizingMask: UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin]; + break; + } + case 2: + { + [_view setContentMode: UIViewContentModeLeft]; + [_view setAutoresizingMask: UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin]; // 0x25 + break; + } } [item setView: _view forManager: self]; @@ -118,14 +90,17 @@ HOOKDEF(void, UIStatusBarForegroundView, _computeVisibleItems$eitherSideItems$, NSMutableArray** visibleItems, NSMutableArray* eitherSideItems) // 3 visible items { -// HookLog(); - + // add our objects to the appropriate arrays - for(int i=0; i<3; i++) + for(int i=0; i<2; i++) // only left+right - center is "virtual" { for(UIStatusBarCustomItem* item in customItems[i]) { - [visibleItems[i] addObject: item]; + NSNumber* visible = [[item properties] objectForKey: @"visible"]; + if(!visible || [visible boolValue]) + { + [visibleItems[i] addObject: item]; + } } } @@ -135,8 +110,6 @@ HOOKDEF(UIView*, UIStatusBarLayoutManager, _viewForItem$creatingIfNecessary$, id item, bool ifNecc) { -// HookLog(); - if([item isKindOfClass: $UIStatusBarCustomItem]) { UIStatusBarItemView* _view = InitializeView(self, item); @@ -144,16 +117,29 @@ } else { - id ret = CALL_ORIG(UIStatusBarLayoutManager, _viewForItem$creatingIfNecessary$, item, ifNecc); + UIView* ret = CALL_ORIG(UIStatusBarLayoutManager, _viewForItem$creatingIfNecessary$, item, ifNecc); return ret; } } +HOOKDEF(UIView*, UIStatusBarLayoutManager, _viewForItem$, id item) +{ + if([item isKindOfClass: $UIStatusBarCustomItem]) + { + UIStatusBarItemView* _view = InitializeView(self, item); + return _view; + } + else + { + UIView* ret = CALL_ORIG(UIStatusBarLayoutManager, _viewForItem$, item); + return ret; + } +} + + HOOKDEF(NSMutableArray*, UIStatusBarLayoutManager, _itemViews) { -// HookLog(); - NSMutableArray* _itemViews = CALL_ORIG(UIStatusBarLayoutManager, _itemViews); // add our array here @@ -165,9 +151,10 @@ for(UIStatusBarCustomItem* item in customItems[_region]) { UIStatusBarItemView* _view = InitializeView(self, item); - if(_view) + { [_itemViews addObject: _view]; + } } } } @@ -177,6 +164,27 @@ +void PrepareEnabledItemsCommon(UIStatusBarLayoutManager* self) +{ + GETVAR(UIStatusBarForegroundView*, _foregroundView); + + float startPosition = [self _startPosition]; + for(UIStatusBarItemView* view in [self _itemViewsSortedForLayout]) + { + if([view superview] == nil) + { + [view setVisible: NO]; + [view setFrame: (CGRect) {{0.0f, 0.0f}, [self _frameForItemView: view startPosition: startPosition].size}]; + [_foregroundView addSubview: view]; + } + int type = [[view item] type]; + if(type) + { + startPosition = [self _positionAfterPlacingItemView: view startPosition: startPosition]; + } + } +} + HOOKDEF(BOOL, UIStatusBarLayoutManager, prepareEnabledItems$, BOOL* items) { SelLog(); @@ -185,124 +193,161 @@ // the default function didn't refresh...let's refresh anyways if(ret==NO) { - - GETVAR(UIStatusBarForegroundView*, _foregroundView); - - float startPosition = [self _startPosition]; - for(UIStatusBarItemView* view in [self _itemViewsSortedForLayout]) - { - if([view superview] == nil) - { - [view setVisible: NO]; - [view setFrame: (CGRect) {{0.0f, 0.0f}, [self _frameForItemView: view startPosition: startPosition].size}]; - [_foregroundView addSubview: view]; - } - int type = [[view item] type]; - if(type) - { - startPosition = [self _positionAfterPlacingItemView: view startPosition: startPosition]; - } - } + PrepareEnabledItemsCommon(self); } return YES; } - - -HOOKDEF(void, UIApplication, addStatusBarImageNamed$removeOnExit$, NSString* name, BOOL removeOnExit) +HOOKDEF(BOOL, UIStatusBarLayoutManager, prepareEnabledItems$withData$actions$, BOOL* items, void* data, int actions) { -// HookLog(); - [[StatusBarItemClient sharedInstance] setProperties: [NSNumber numberWithInt: 1] forItem: name]; + SelLog(); + BOOL ret = CALL_ORIG(UIStatusBarLayoutManager, prepareEnabledItems$withData$actions$, items, data, actions); + + // the default function didn't refresh...let's refresh anyways + if(ret==NO) + { + PrepareEnabledItemsCommon(self); + } + return YES; + } -HOOKDEF(void, UIApplication, addStatusBarImageNamed$, NSString* name) -{ -// HookLog(); - [[StatusBarItemClient sharedInstance] setProperties: [NSNumber numberWithInt: 1] forItem: name]; -} -HOOKDEF(void, UIApplication, removeStatusBarImageNamed$, NSString* name) -{ -// HookLog(); - [[StatusBarItemClient sharedInstance] setProperties: nil forItem: name]; -} +#pragma mark UIStatusBarTimeItemView modifications (for center text) -// used for testing only -/* -void addPause() +HOOKDEF(BOOL, UIStatusBarTimeItemView, updateForNewData$actions$, void* data, int actions) { - [UIApp addStatusBarImageNamed: @"Pause"]; + NSString* &_timeString(MSHookIvar(self, "_timeString")); + NSString* oldString = [_timeString retain]; + + // retrieve the current string index + int idx; + { + uint64_t value; + const char* notif = "libstatusbar_changed"; + int token = 0; + notify_register_check(notif, &token); + notify_get_state(token, &value); + + idx = value; + } + + // Fetch the current string + _timeString = [[[LSStatusBarClient sharedInstance] titleStringAtIndex: idx] retain]; + + // I guess not. Fetch the default string + if(!_timeString) + { + CALL_ORIG(UIStatusBarTimeItemView, updateForNewData$actions$, data, actions); + } + + // Did the string change? + bool isSame = [oldString isEqualToString: _timeString]; + [oldString release]; + return !isSame; } -void removePause() -{ - [UIApp removeStatusBarImageNamed: @"Pause"]; -} +/* +@interface UIStatusBar : NSObject +- (CGRect) currentFrame; +@end +*/ -void addBT() +HOOKDEF(UIImage*, UIStatusBarTimeItemView, contentsImageForStyle$, int style) { - [UIApp addStatusBarImageNamed: @"Bluetooth"]; -} + NSString* &_timeString(MSHookIvar(self, "_timeString")); -void removeBT() -{ - [UIApp removeStatusBarImageNamed: @"Bluetooth"]; + NSMutableString* timeString = [_timeString mutableCopy]; + + CGSize size = [(UIStatusBar*)[UIApp statusBar] currentFrame].size; + float maxlen = (size.width > size.height ? size.width : size.height)*0.65; + + // ellipsize strings if they're too long + if([timeString sizeWithFont: (UIFont*) [self textFont]].width > maxlen) + { + [timeString replaceCharactersInRange: (NSRange){[timeString length]-1, 1} withString: @"…"]; + while([timeString length]>3 && [timeString sizeWithFont: (UIFont*) [self textFont]].width > maxlen) + { + [timeString replaceCharactersInRange: (NSRange){[timeString length]-2, 1} withString: @""]; + } + } + + // string swap + NSString* oldTimeString = _timeString; + _timeString = [timeString retain]; // neccessary ? + + UIImage* ret = CALL_ORIG(UIStatusBarTimeItemView, contentsImageForStyle$, style); + + // string swap + _timeString = oldTimeString; + [timeString release]; + + return ret; } -*/ + + +#pragma mark Client startup HOOKDEF(void, UIApplication, _startWindowServerIfNecessary) { HookLog(); CALL_ORIG(UIApplication, _startWindowServerIfNecessary); + static BOOL hasAlreadyRan = NO; + if(hasAlreadyRan) + { + NSLog(@"Warning: _startWindowServerIfNecessary called twice!"); + return; + } + hasAlreadyRan = YES; + // use this only for starting client // register as client - make sure SpringBoard is running // UIKit should still not exist.../yet/ if($SpringBoard || SBSSpringBoardServerPort()) { - [StatusBarItemClient sharedInstance]; + [LSStatusBarClient sharedInstance]; } NSLine(); - - // testing stuff... - /* - { - float delay = 4.0f; - CFRunLoopTimerCallBack callback = (CFRunLoopTimerCallBack) addBT; - - CFRunLoopTimerRef waitTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+delay, 0.0f, 0, 0, callback, NULL); - CFRunLoopAddTimer(CFRunLoopGetMain(), waitTimer, kCFRunLoopCommonModes); - } - { - float delay = 8.0f; - CFRunLoopTimerCallBack callback = (CFRunLoopTimerCallBack) addPause; - - - CFRunLoopTimerRef waitTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+delay, 0.0f, 0, 0, callback, NULL); - CFRunLoopAddTimer(CFRunLoopGetMain(), waitTimer, kCFRunLoopCommonModes); - } - - { - float delay = 12.0f; - CFRunLoopTimerCallBack callback = (CFRunLoopTimerCallBack) removeBT; - - - CFRunLoopTimerRef waitTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+delay, 0.0f, 0, 0, callback, NULL); - CFRunLoopAddTimer(CFRunLoopGetMain(), waitTimer, kCFRunLoopCommonModes); - } - { - float delay = 16.0f; - CFRunLoopTimerCallBack callback = (CFRunLoopTimerCallBack) addBT; - - - CFRunLoopTimerRef waitTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+delay, 0.0f, 0, 0, callback, NULL); - CFRunLoopAddTimer(CFRunLoopGetMain(), waitTimer, kCFRunLoopCommonModes); - } - */ } + + + +#pragma mark 3.x Compatibility Hooks + +HOOKDEF(void, UIApplication, addStatusBarImageNamed$removeOnExit$, NSString* name, BOOL removeOnExit) +{ + [[LSStatusBarClient sharedInstance] setProperties: [NSNumber numberWithInt: 1] forItem: name]; +} + +HOOKDEF(void, UIApplication, addStatusBarImageNamed$, NSString* name) +{ + [[LSStatusBarClient sharedInstance] setProperties: [NSNumber numberWithInt: 1] forItem: name]; +} + +HOOKDEF(void, UIApplication, removeStatusBarImageNamed$, NSString* name) +{ + [[LSStatusBarClient sharedInstance] setProperties: nil forItem: name]; +} + + + + + +//@class UIStatusBarTimeItemView; + + + +@class SBApplication; + +HOOKDEF(void, SBApplication, exitedCommon) +{ + [[LSStatusBarServer sharedInstance] appDidExit: [self bundleIdentifier]]; +} + __attribute__((constructor)) void start() { NSLine(); @@ -319,32 +364,44 @@ void removeBT() ClassCreate_UIStatusBarCustomItem(); { HOOKCLASSMESSAGE(UIStatusBarItem, itemWithType:, itemWithType$); -// HOOKCLASSMESSAGE(UIStatusBarItem, itemType:canBeEnabledForData:, itemType$canBeEnabledForData$); } { HOOKMESSAGE(UIStatusBarForegroundView, _computeVisibleItems:eitherSideItems:, _computeVisibleItems$eitherSideItems$); } { - HOOKMESSAGE(UIStatusBarLayoutManager, _viewForItem:creatingIfNecessary:, _viewForItem$creatingIfNecessary$); + if([$UIStatusBarLayoutManager instancesRespondToSelector: @selector(_viewForItem:creatingIfNecessary:)]) + HOOKMESSAGE(UIStatusBarLayoutManager, _viewForItem:creatingIfNecessary:, _viewForItem$creatingIfNecessary$); + if([$UIStatusBarLayoutManager instancesRespondToSelector: @selector(prepareEnabledItems:)]) + HOOKMESSAGE(UIStatusBarLayoutManager, prepareEnabledItems:, prepareEnabledItems$); + + if([$UIStatusBarLayoutManager instancesRespondToSelector: @selector(_viewForItem:)]) + HOOKMESSAGE(UIStatusBarLayoutManager, _viewForItem:, _viewForItem$); + if([$UIStatusBarLayoutManager instancesRespondToSelector: @selector(prepareEnabledItems:withData:actions:)]) + HOOKMESSAGE(UIStatusBarLayoutManager, prepareEnabledItems:withData:actions:, prepareEnabledItems$withData$actions$); + HOOKMESSAGE(UIStatusBarLayoutManager, _itemViews, _itemViews); - HOOKMESSAGE(UIStatusBarLayoutManager, prepareEnabledItems:, prepareEnabledItems$); + + HOOKMESSAGE(UIStatusBarTimeItemView, updateForNewData:actions:, updateForNewData$actions$); + HOOKMESSAGE(UIStatusBarTimeItemView, contentsImageForStyle:, contentsImageForStyle$); + } + { HOOKMESSAGE(UIApplication, addStatusBarImageNamed:removeOnExit:, addStatusBarImageNamed$removeOnExit$); HOOKMESSAGE(UIApplication, addStatusBarImageNamed:, addStatusBarImageNamed$); HOOKMESSAGE(UIApplication, removeStatusBarImageNamed:, removeStatusBarImageNamed$); - HOOKMESSAGE(UIApplication, _startWindowServerIfNecessary, _startWindowServerIfNecessary); + HOOKCLASSMESSAGE(UIApplication, _startWindowServerIfNecessary, _startWindowServerIfNecessary); } if($SpringBoard) { - [StatusBarItemServer sharedInstance]; + [LSStatusBarServer sharedInstance]; + + GETCLASS(SBApplication); + HOOKMESSAGE(SBApplication, exitedCommon, exitedCommon); } - - NSLine(); - } }