├── .gitignore ├── ListLauncher9.plist ├── .gitmodules ├── control ├── Makefile └── Tweak.xm /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.stamp 3 | .theos 4 | .DS_Store 5 | theos 6 | _ 7 | obj 8 | debs -------------------------------------------------------------------------------- /ListLauncher9.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.springboard" ); }; } 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "NearbyNews"] 2 | path = NearbyNews 3 | url = https://github.com/twodayslate/NearbyNews.git 4 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: org.thebigboss.listlauncher9 2 | Name: ListLauncher for iOS 9 3 | Depends: mobilesubstrate 4 | Conflicts: org.thebigboss.nearbynews 5 | Version: 1.0 6 | Architecture: iphoneos-arm 7 | Description: Appliciation list inside of Spotlight! Launch apps fast! 8 | Maintainer: twodayslate 9 | Author: twodayslate 10 | Section: Tweaks 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | THEOS_DEVICE_IP = 127.0.0.1 2 | THEOS_DEVICE_PORT = 2222 3 | 4 | include $(THEOS)/makefiles/common.mk 5 | 6 | TWEAK_NAME = ListLauncher9 7 | ListLauncher9_FILES = Tweak.xm 8 | 9 | include $(THEOS_MAKE_PATH)/tweak.mk 10 | 11 | SUBPROJECTS += NearbyNews 12 | 13 | include $(THEOS_MAKE_PATH)/aggregate.mk 14 | 15 | after-install:: 16 | install.exec "killall -9 SpringBoard" 17 | -------------------------------------------------------------------------------- /Tweak.xm: -------------------------------------------------------------------------------- 1 | @interface ALApplicationList 2 | + (ALApplicationList *)sharedApplicationList; 3 | - (NSDictionary *)applicationsFilteredUsingPredicate:(NSPredicate *)predicate onlyVisible:(BOOL)onlyVisible titleSortedIdentifiers:(NSArray **)outSortedByTitle; 4 | - (id)valueForKey:(NSString *)key forDisplayIdentifier:(NSString *)displayIdentifier; 5 | @end 6 | 7 | @interface SPSearchResult : NSObject 8 | @property (nonatomic,retain) NSString * fbr; 9 | @property (nonatomic,retain) NSString * templateName; 10 | @property (nonatomic,retain) NSString * card_title; 11 | @property (assign,nonatomic) int flags; //@synthesize flags=_flags - In the implementation block 12 | -(void)setTitle:(NSString *)arg1 ; 13 | -(void)setSearchResultDomain:(unsigned)arg1 ; 14 | -(void)setBundleID:(NSString *)arg1 ; 15 | -(void)setExternalIdentifier:(NSString *)arg1 ; 16 | -(void)setHasAssociatedUserActivity:(BOOL)arg1 ; 17 | -(void)setUserActivityEligibleForPublicIndexing:(BOOL)arg1 ; 18 | -(void)setUrl:(NSString *)arg1 ; 19 | @end 20 | 21 | @interface SPSearchResultSection 22 | @property (nonatomic, retain) NSString *displayIdentifier; 23 | @property (nonatomic) unsigned int domain; 24 | @property (nonatomic, retain) NSString *category; 25 | - (void)addResults:(SPSearchResult *)arg1; 26 | - (id)results; 27 | - (id)resultsAtIndex:(unsigned int)arg1; 28 | @end 29 | 30 | @interface SPUISearchViewController : UIViewController 31 | @property (assign, nonatomic) BOOL isShowingListLauncher; 32 | @property (assign, nonatomic) BOOL isActuallyPullDown; 33 | - (BOOL)_hasResults; 34 | - (id)searchTableView; 35 | + (id)sharedInstance; 36 | - (id)currentSearchModel; 37 | - (void)_clearSearchResults; 38 | - (void)_reloadTable; 39 | - (BOOL)_isPullDownSpotlight; 40 | - (BOOL)isZKWSearchMode; 41 | - (BOOL)_hasNoQuery; 42 | @end 43 | 44 | 45 | 46 | @interface SPUISearchTableView : UITableView 47 | - (BOOL)sectionIsExpanded:(int)arg1; 48 | - (void)toggleExpansionForSection:(unsigned int)arg1; 49 | @end 50 | 51 | @class SPUISearchModel; 52 | @interface SPUISearchModel 53 | + (id)sharedFullZWKInstance; 54 | + (id)sharedGeneralInstance; 55 | + (id)sharedInstance; 56 | + (id)sharedPartialZKWInstance; 57 | - (void)addSections:(NSMutableArray *)arg1; 58 | - (void)handleHiddenResult:(id)arg1 shownResult:(id)arg2 inSection:(id)arg3; 59 | - (void)clear; 60 | - (void)clearInternal:(int)arg1; 61 | - (void)clearParsecResultsIfStale; 62 | - (void)searchDaemonQuery:(id)arg1 addedResults:(id)arg2; 63 | - (BOOL)shouldHideSection:(NSMutableArray *)arg1; 64 | @end 65 | 66 | @interface SPUISearchTableHeaderView : UITableViewHeaderFooterView 67 | @property (nonatomic, retain) UILabel *titleLabel; 68 | - (void)setMoreButtonVisible:(BOOL)arg1; 69 | - (void)setTitleLabel:(UILabel *)arg1; 70 | - (void)updateWithTitle:(id)arg1 section:(unsigned int)arg2 isExpanded:(BOOL)arg3; 71 | @end 72 | 73 | @interface SPUISearchHeader : UIView 74 | @property (nonatomic,retain,readonly) UITextField * searchField; 75 | @end 76 | 77 | static BOOL hasCreatedSectionsOnce = NO; 78 | static NSArray *sortedDisplayIdentifiers = nil; 79 | static NSMutableArray *sectionIndexTitles = nil; 80 | static NSMutableArray *sectionIndexes = nil; 81 | 82 | static NSMutableArray* createSections() { 83 | HBLogDebug(@"Creating sections!"); 84 | 85 | sectionIndexTitles = [[NSMutableArray array] retain]; 86 | sectionIndexes = [[NSMutableArray array] retain]; 87 | NSMutableArray *sortedDisplayNames = [NSMutableArray array]; 88 | sectionIndexTitles = [[NSMutableArray array] retain]; 89 | 90 | ALApplicationList *applications = [%c(ALApplicationList) sharedApplicationList]; 91 | NSDictionary *appListResults = [applications applicationsFilteredUsingPredicate:nil onlyVisible:YES titleSortedIdentifiers:&sortedDisplayIdentifiers]; 92 | [sortedDisplayIdentifiers retain]; 93 | 94 | SPSearchResultSection *newSection = [[%c(SPSearchResultSection) alloc] init]; 95 | [newSection setDisplayIdentifier:@"My Applications"]; 96 | [newSection setCategory:@"My Applications"]; 97 | [newSection setDomain:4]; 98 | 99 | for(NSString *displayID in sortedDisplayIdentifiers) { 100 | //HBLogDebug(@"%@", displayID); 101 | NSString *displayName = [appListResults objectForKey:displayID]; 102 | [sortedDisplayNames addObject:displayName]; 103 | NSString * firstLetter = [[displayName substringWithRange:[displayName rangeOfComposedCharacterSequenceAtIndex:0]] uppercaseString]; 104 | NSCharacterSet *alphaSet = [NSCharacterSet letterCharacterSet]; 105 | BOOL valid = [[firstLetter stringByTrimmingCharactersInSet:alphaSet] isEqualToString:@""]; 106 | if(!valid) { 107 | firstLetter = @"#"; 108 | } 109 | 110 | if(![sectionIndexTitles containsObject:firstLetter]) { 111 | [sectionIndexTitles addObject:firstLetter]; 112 | // now an index 113 | [sectionIndexes addObject:[NSNumber numberWithInteger:[sortedDisplayNames indexOfObject:displayName]]]; 114 | } 115 | 116 | SPSearchResult *myOtherCustomThing = [[%c(SPSearchResult) alloc] init]; 117 | [myOtherCustomThing setTitle:displayName]; 118 | [myOtherCustomThing setSearchResultDomain:4]; 119 | [myOtherCustomThing setBundleID:displayID]; 120 | [myOtherCustomThing setExternalIdentifier:displayID]; 121 | [myOtherCustomThing setUrl:displayID]; 122 | myOtherCustomThing.templateName = @"generic"; 123 | myOtherCustomThing.card_title = @"My Applications"; 124 | [myOtherCustomThing setHasAssociatedUserActivity:NO]; 125 | [myOtherCustomThing setUserActivityEligibleForPublicIndexing:NO]; 126 | 127 | [newSection addResults:myOtherCustomThing]; 128 | } 129 | 130 | NSMutableArray *rar = [NSMutableArray array]; 131 | [rar addObject:newSection]; 132 | 133 | hasCreatedSectionsOnce = YES; 134 | 135 | HBLogDebug(@"has completed creation"); 136 | return rar; 137 | } 138 | 139 | 140 | //This works too but there are some annoying bugs that are too hard to fix correctly so I don't do this. 141 | static BOOL canDeclare = NO; 142 | 143 | %hook SPUISearchModel 144 | 145 | + (id)sharedPartialZKWInstance { 146 | if(canDeclare) { 147 | return [self sharedFullZWKInstance]; 148 | } 149 | canDeclare = YES; 150 | return %orig; 151 | } 152 | 153 | %new 154 | -(BOOL)shouldHideSection:(NSMutableArray *)arg1 { 155 | SPUISearchViewController *sharedvc = [%c(SPUISearchViewController) sharedInstance]; 156 | 157 | if([sharedvc _hasNoQuery]) { 158 | return YES; 159 | } 160 | 161 | if(![sharedvc _hasResults]) { 162 | SPSearchResultSection *firstSection = [arg1 objectAtIndex:0]; 163 | if(firstSection.domain == 1) { // Web Search 164 | return YES; 165 | } 166 | } 167 | 168 | return NO; 169 | } 170 | 171 | - (void)addSections:(NSMutableArray *)arg1 { 172 | //%log; 173 | HBLogDebug(@"adding sections"); 174 | SPUISearchViewController *sharedvc = [%c(SPUISearchViewController) sharedInstance]; 175 | if(![self shouldHideSection:arg1]) { 176 | sharedvc.isShowingListLauncher = NO; 177 | %orig; 178 | } else { 179 | sharedvc.isShowingListLauncher = YES; 180 | %orig(createSections()); 181 | } 182 | } 183 | 184 | %end 185 | 186 | %hook SPUISearchViewController 187 | // -(id)init { 188 | // %log; 189 | // listLauncherSection = createSections(); 190 | // return %orig; 191 | // } 192 | - (BOOL)_hasResults { 193 | return YES; 194 | } 195 | 196 | %property (assign, nonatomic) BOOL isShowingListLauncher; 197 | - (void)_didFinishPresenting { 198 | %log; 199 | %orig; 200 | 201 | if(self.isShowingListLauncher) { 202 | SPUISearchTableView *tableview = MSHookIvar(self, "_tableView"); 203 | if(![tableview sectionIsExpanded:0]) { 204 | [tableview toggleExpansionForSection:0]; 205 | } 206 | 207 | SPUISearchTableHeaderView *headerView = (SPUISearchTableHeaderView *) [tableview headerViewForSection:0]; 208 | [headerView setMoreButtonVisible:NO]; 209 | [headerView updateWithTitle:@"LISTLAUNCHER" section:0 isExpanded:YES]; 210 | } 211 | } 212 | 213 | - (BOOL)_showKeyboardOnPresentation { 214 | %log; 215 | if(self.isShowingListLauncher) { 216 | return NO; 217 | } 218 | return %orig; 219 | } 220 | - (int)numberOfPossibleRowsInSection:(int)arg1 { 221 | %log; 222 | 223 | if(self.isShowingListLauncher && sortedDisplayIdentifiers && hasCreatedSectionsOnce) { 224 | HBLogDebug(@"sortedDisplayIdentifiers = %@", [sortedDisplayIdentifiers class]); 225 | return [sortedDisplayIdentifiers count]; 226 | } 227 | 228 | return %orig; 229 | } 230 | 231 | %new 232 | -(NSArray *)sectionIndexTitlesForTableView:(UITableView *)arg1 { 233 | %log; 234 | if(self.isShowingListLauncher && hasCreatedSectionsOnce && sectionIndexTitles && arg1.numberOfSections > 0) { 235 | //HBLogDebug(@"indexes = %@", sectionIndexTitles); 236 | HBLogDebug(@"sortedDisplayIdentifiers = %@", [sectionIndexTitles class]); 237 | 238 | return (NSArray *)[sectionIndexTitles copy]; 239 | } 240 | return nil; 241 | } 242 | 243 | %new 244 | -(int)tableView:(UITableView *)tableview sectionForSectionIndexTitle:(id)title atIndex:(int)index { 245 | %log; 246 | 247 | SPUISearchHeader *sheader = MSHookIvar(self, "_searchHeader"); 248 | [sheader.searchField resignFirstResponder]; 249 | 250 | if([title isEqualToString:@"#"]) return 0; 251 | [tableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[[sectionIndexes objectAtIndex:index] integerValue] inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; 252 | return 99999999; // this allows for scrolling without jumping to some random ass section 253 | } 254 | 255 | 256 | %end 257 | 258 | %hook SPUISearchTableHeaderView 259 | - (void)updateWithTitle:(id)arg1 section:(unsigned int)arg2 isExpanded:(BOOL)arg3 { 260 | %log; 261 | SPUISearchViewController *sharedvc = [%c(SPUISearchViewController) sharedInstance]; 262 | if(sharedvc.isShowingListLauncher) { 263 | [self setMoreButtonVisible:NO]; 264 | arg1 = @"LISTLAUNCHER"; 265 | arg3 = YES; 266 | } 267 | %orig; 268 | } 269 | - (void)setMoreButtonVisible:(BOOL)arg1 { 270 | SPUISearchViewController *sharedvc = [%c(SPUISearchViewController) sharedInstance]; 271 | 272 | if(sharedvc.isShowingListLauncher) { 273 | %orig(NO); 274 | } else { 275 | %orig; 276 | } 277 | } 278 | %end 279 | 280 | 281 | %hook SPUISearchTableView 282 | - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { 283 | %log; 284 | self = %orig; 285 | self.sectionIndexColor = [UIColor whiteColor]; // index text 286 | self.sectionIndexTrackingBackgroundColor = [UIColor clearColor]; //bg touched 287 | self.sectionIndexBackgroundColor = [UIColor clearColor]; //bg touched 288 | return self; 289 | } 290 | - (void)clearExpansionState { 291 | %log; 292 | SPUISearchViewController *sharedvc = [%c(SPUISearchViewController) sharedInstance]; 293 | if(!sharedvc.isShowingListLauncher) { 294 | %orig; 295 | } 296 | } 297 | - (void)setExpandedSections:(id)arg1 { 298 | %log; 299 | %orig; 300 | } 301 | - (void)toggleExpansionForSection:(unsigned int)arg1 { 302 | %log; 303 | %orig; 304 | } 305 | 306 | %end 307 | 308 | static id observer; 309 | %ctor { 310 | dlopen("/Library/MobileSubstrate/DynamicLibraries/AppList.dylib", RTLD_NOW); 311 | // Wait to do UI stuff 312 | // http://iphonedevwiki.net/index.php/User:Uroboro#UI_usage 313 | observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification 314 | object:nil queue:[NSOperationQueue mainQueue] 315 | usingBlock:^(NSNotification *notification) { 316 | if(!hasCreatedSectionsOnce) { 317 | HBLogDebug(@"Should be first create hopefully!"); 318 | sortedDisplayIdentifiers = nil; 319 | sectionIndexTitles = [NSMutableArray array]; 320 | sectionIndexes = [NSMutableArray array]; 321 | createSections(); 322 | } 323 | } 324 | ]; 325 | } 326 | 327 | %dtor { 328 | [[NSNotificationCenter defaultCenter] removeObserver:observer]; 329 | } --------------------------------------------------------------------------------