/* * Five-Icon SpringBoard (FCSB) 0.59 by Yanik Magnan * http://r-ch.net/iphone/ Copyright (c) 2008-2009, Yanik Magnan Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "substrate.h" #import #import #include "SpringBoard/SBIconList.h" #include "SpringBoard/SBIcon.h" #include "SpringBoard/SBIconModel.h" #include "SpringBoard/SBImageCache.h" #include "SpringBoard/SBApplication.h" #include "SpringBoard/SBApplicationIcon.h" #include @class SBApplication; @protocol FiveColumnSB - (int) fcsb_maxIconColumns; - (int) fcsb_maxIconRows; - (CGPoint) fcsb_originForIconAtX:(int)x Y:(int)y; - (id) fcsb_iconForDisplayIdentifier:(id)key; - (NSString*) fcsb_pathForIcon; - (id) fcsb_exportState; - (void) fcsb_importState:(id)state; - (id) fcsb_iconState; - (void) fcsb_writeIconState; @end static BOOL isDock(SBIconList* iL) { return [iL isKindOfClass:NSClassFromString(@"SBButtonBar")]; } static BOOL isEnabled() { // Checks to see if FCSB is enabled. // Note that the code that handles uninstalls and whatnot is separate from the extension, // merely changing the on to off will cause icons to be rearranged as the firmware will // notice the dimensions of the icon matrices are off and will regenerate its settings // plist. NSDictionary* settingsDict = [NSDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/net.r-ch.fcsb.plist"]; if (settingsDict) return [[settingsDict objectForKey:@"Enable"] boolValue]; return YES; } static int $SBIconList$maxIconColumns(SBIconList *self, SEL sel) { // The obvious hack everyone would have tried first. Turns out this works, // except the 5th icon overflows onto the second row, which is why we need // the setOrigin hook. if(isDock(self)) return [self fcsb_maxIconColumns]; return 5; } static int $SBIconList$maxIconRows(SBIconList *self, SEL sel) { // I once again vaguely remember stuff going horribly wrong if this wasn't here in 2.x. // This might conflict with other column/row extensions; no idea. I don't use them. :/ if(isDock(self)) return [self fcsb_maxIconRows]; return 4; } static CGPoint $SBIconList$originForIconAtX$Y$(SBIconList *self, SEL sel, int x, int y) { // Completely misleading method name. // The "x" and "y" variables are the columns and rows the icons are on, respectively. // Therefore, the icon in the first column would have an x of 0, and the last would be 4. // This needs to be overridden in 3.0 to get the icons to align with Five Icon Dock. if (isDock(self)) return [self fcsb_originForIconAtX:x Y:y]; // Don't touch the dock, let FID deal with that. CGPoint origin; CGPoint orig_origin = [self fcsb_originForIconAtX:x Y:y]; origin.x = 4 + (x * 63); // Apple, why did you remove leftMarginForIconRowArray? frowny pants :( origin.y = orig_origin.y; return origin; } /* Pretty iTunes Message */ static BOOL isAMessagePartID(NSString* displayID) { return [displayID hasPrefix:@"net.r-ch.fcsb.itunes9sucks.messagept"]; } static NSString* bundleIDForPart(int part) { return [NSString stringWithFormat:@"net.r-ch.fcsb.itunes9sucks.messagept%i", part]; } static NSString* $SBApplication$pathForIcon(SBApplication *self, SEL sel) { if (!isAMessagePartID([self bundleIdentifier])) return [self fcsb_pathForIcon]; int part = [[[self bundleIdentifier] substringFromIndex:36] intValue]; return [NSString stringWithFormat:@"/Library/FCSB/iTunesIcons/%i.png", part]; } static id applicationForBundleID(NSString* bundleID) { return [[objc_getClass("SBApplication") alloc] initWithBundleIdentifier:bundleID roleIdentifier:nil path:nil bundle:nil infoDictionary:nil isSystemApplication:YES signerIdentity:nil provisioningProfileValidated:YES]; } static id $SBIconModel$iconForDisplayIdentifier$(SBImageCache *self, SEL sel, id displayID) { if (!isAMessagePartID(displayID)) return [self fcsb_iconForDisplayIdentifier:displayID]; // Create custom icon objects for iTunes message objects return [[[objc_getClass("SBApplicationIcon") alloc] initWithApplication:[applicationForBundleID(displayID) autorelease]] autorelease]; } /* iTunes App Arrangement Communication */ static NSDictionary* dictRepresentationForPart(int part) { // Formats a part of the FCSB iTunes sync message into a format it can understand. NSMutableDictionary* dictRepr = [NSMutableDictionary new]; [dictRepr setObject:bundleIDForPart(part) forKey:@"bundleIdentifier"]; [dictRepr setObject:bundleIDForPart(part) forKey:@"displayIdentifier"]; [dictRepr setObject:@" " forKey:@"displayName"]; [dictRepr setObject:[NSDate date] forKey:@"iconModDate"]; return [dictRepr autorelease]; } static BOOL $SBIconModel$importState$(SBIconModel *self, SEL sel, id importedState) { // Simply ignore any incoming layout changes made by iTunes. return NO; } static id $SBIconModel$exportState(SBIconModel *self, SEL sel) { NSArray* originalState = [self fcsb_exportState]; // Extract the dock and keep it identical NSArray* dock = [originalState objectAtIndex:0]; // Prepare an array to hold all icons' dictionary representations NSMutableArray* holdAllIcons = [[NSMutableArray alloc] init]; NSArray* iconLists = [originalState subarrayWithRange:NSMakeRange(1,[originalState count]-1)]; for (NSArray* page in iconLists) { for (NSArray* row in page) { for (id iconDict in row) { if ([iconDict isKindOfClass:[NSDictionary class]]) [holdAllIcons addObject:iconDict]; } } } // Prepend an array of 4 message icon dictionary representations NSMutableArray* messageIcons = [[NSMutableArray alloc] init]; for (int i=1; i <= 4; i++) { [messageIcons addObject:dictRepresentationForPart(i)]; } [holdAllIcons insertObjects:messageIcons atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,4)]]; [messageIcons release]; // Add the padding to the end of the array while (([holdAllIcons count] % 16) != 0) { [holdAllIcons addObject:[NSNumber numberWithInt:0]]; } // Split this huge array into 4x4 pages/rows NSMutableArray* allPages = [[NSMutableArray alloc] init]; [allPages addObject:dock]; int totalPages = ceil([holdAllIcons count] / 16.0); for (int i=0; i < totalPages; i++) { int firstIndex = i * 16; // Get an array representing all of that pages' icons NSArray* thisPage = [holdAllIcons subarrayWithRange:NSMakeRange(firstIndex, 16)]; NSMutableArray* newPage = [[NSMutableArray alloc] init]; for (int j=0; j < 4; j++) { // Number of rows NSArray* thisRow = [thisPage subarrayWithRange:NSMakeRange(j*4, 4)]; [newPage addObject:thisRow]; } [allPages addObject:newPage]; [newPage release]; } [holdAllIcons release]; return [allPages autorelease]; } /* No More Uninstall Process To Screw Up (or Messed Up Layouts Either!!); FCSB Keeps Its Own Layout Now :> Completely ripped out of Iconoclasm. */ static id representation(id iconListOrDock) { // Returns a dictionary representation of an icon list or dock, // as it varies depending on the OS version installed. if ([iconListOrDock respondsToSelector:@selector(representation)]) return [iconListOrDock representation]; else if ([iconListOrDock respondsToSelector:@selector(dictionaryRepresentation)]) return [iconListOrDock dictionaryRepresentation]; return nil; } static id $SBIconModel$iconState(SBIconModel *self, SEL sel) { NSDictionary*& previousIconState = MSHookIvar(self, "_previousIconState"); if (previousIconState == nil) { NSDictionary* springBoardPlist = [NSDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist"]; id newIconState = [[springBoardPlist objectForKey:@"iconState_fcsb"] mutableCopy]; if (newIconState) // If FCSB has a layout saved already, go ahead and return that. return [newIconState autorelease]; } return [self fcsb_iconState]; // Otherwise, just send SpringBoard's and we'll copy it (and adapt it for 5x5 of course) } static void $SBIconModel$_writeIconState(SBIconModel *self, SEL sel) { // Write the icon state to disc in a separate key from SpringBoard's 4x4 default key NSMutableDictionary* newState = [[NSMutableDictionary alloc] init]; [newState setObject:representation([self buttonBar]) forKey:@"buttonBar"]; NSMutableArray* lists = [[NSMutableArray alloc] init]; for (SBIconList* iconList in [self iconLists]) { [lists addObject:representation(iconList)]; } [newState setObject:lists forKey:@"iconLists"]; [lists release]; NSMutableDictionary* springBoardPlist = [[NSDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist"] mutableCopy]; [springBoardPlist setObject:newState forKey:@"iconState_fcsb"]; [newState release]; [springBoardPlist writeToFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist" atomically:YES]; [springBoardPlist release]; } extern "C" void TweakInitialize() { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; if ((objc_getClass("SpringBoard") == nil) || (isEnabled() == NO)) { [pool release]; return; } Class $SBIconList(objc_getClass("SBIconList")); MSHookMessage($SBIconList, @selector(maxIconColumns), (IMP) &$SBIconList$maxIconColumns, "fcsb_"); MSHookMessage($SBIconList, @selector(maxIconRows), (IMP) &$SBIconList$maxIconRows, "fcsb_"); MSHookMessage($SBIconList, @selector(originForIconAtX:Y:), (IMP) &$SBIconList$originForIconAtX$Y$, "fcsb_"); Class $SBIconModel(objc_getClass("SBIconModel")); MSHookMessage($SBIconModel, @selector(iconForDisplayIdentifier:), (IMP) &$SBIconModel$iconForDisplayIdentifier$, "fcsb_"); MSHookMessage($SBIconModel, @selector(exportState), (IMP) &$SBIconModel$exportState, "fcsb_"); MSHookMessage($SBIconModel, @selector(importState:), (IMP) &$SBIconModel$importState$, "fcsb_"); MSHookMessage($SBIconModel, @selector(iconState), (IMP) &$SBIconModel$iconState, "fcsb_"); MSHookMessage($SBIconModel, @selector(_writeIconState), (IMP) &$SBIconModel$_writeIconState, "fcsb_"); Class $SBApplication(objc_getClass("SBApplication")); MSHookMessage($SBApplication, @selector(pathForIcon), (IMP) &$SBApplication$pathForIcon, "fcsb_"); [pool release]; }