]>
Commit | Line | Data |
---|---|---|
1 | /* WinterBoard - Theme Manager for the iPhone | |
2 | * Copyright (C) 2008-2014 Jay Freeman (saurik) | |
3 | */ | |
4 | ||
5 | /* GNU Lesser General Public License, Version 3 {{{ */ | |
6 | /* | |
7 | * WinterBoard is free software: you can redistribute it and/or modify it under | |
8 | * the terms of the GNU Lesser General Public License as published by the | |
9 | * Free Software Foundation, either version 3 of the License, or (at your | |
10 | * option) any later version. | |
11 | * | |
12 | * WinterBoard is distributed in the hope that it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public | |
15 | * License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public License | |
18 | * along with WinterBoard. If not, see <http://www.gnu.org/licenses/>. | |
19 | **/ | |
20 | /* }}} */ | |
21 | ||
22 | #import <Foundation/Foundation.h> | |
23 | #import <UIKit/UIKit.h> | |
24 | #import <Preferences/PSRootController.h> | |
25 | #import <Preferences/PSViewController.h> | |
26 | #import <Preferences/PSListController.h> | |
27 | #import <Preferences/PSSpecifier.h> | |
28 | #import <Preferences/PSTableCell.h> | |
29 | #import <UIKit/UINavigationButton.h> | |
30 | ||
31 | #include <dlfcn.h> | |
32 | #include <objc/runtime.h> | |
33 | ||
34 | extern NSString *PSTableCellKey; | |
35 | extern "C" UIImage *_UIImageWithName(NSString *); | |
36 | ||
37 | static UIImage *checkImage; | |
38 | static UIImage *uncheckedImage; | |
39 | ||
40 | static BOOL settingsChanged; | |
41 | static NSMutableDictionary *_settings; | |
42 | static NSString *_plist; | |
43 | ||
44 | void AddThemes(NSMutableArray *themesOnDisk, NSString *folder) { | |
45 | NSArray *themes([[NSFileManager defaultManager] contentsOfDirectoryAtPath:folder error:NULL]); | |
46 | for (NSString *theme in themes) { | |
47 | if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/%@/Info.plist", folder, theme]]) { | |
48 | if (NSArray *version = [info objectForKey:@"CoreFoundationVersion"]) { | |
49 | size_t count([version count]); | |
50 | if (count == 0 || count > 2) | |
51 | continue; | |
52 | ||
53 | double lower([[version objectAtIndex:0] doubleValue]); | |
54 | if (kCFCoreFoundationVersionNumber < lower) | |
55 | continue; | |
56 | ||
57 | if (count != 1) { | |
58 | double upper([[version objectAtIndex:1] doubleValue]); | |
59 | if (upper <= kCFCoreFoundationVersionNumber) | |
60 | continue; | |
61 | } | |
62 | } | |
63 | } | |
64 | ||
65 | [themesOnDisk addObject:theme]; | |
66 | } | |
67 | } | |
68 | ||
69 | /* [NSObject yieldToSelector:(withObject:)] {{{*/ | |
70 | @interface NSObject (wb$yieldToSelector) | |
71 | - (id) wb$yieldToSelector:(SEL)selector withObject:(id)object; | |
72 | - (id) wb$yieldToSelector:(SEL)selector; | |
73 | @end | |
74 | ||
75 | @implementation NSObject (Cydia) | |
76 | ||
77 | - (void) wb$doNothing { | |
78 | } | |
79 | ||
80 | - (void) wb$_yieldToContext:(NSMutableArray *)context { | |
81 | NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); | |
82 | ||
83 | SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue])); | |
84 | id object([[context objectAtIndex:1] nonretainedObjectValue]); | |
85 | volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue])); | |
86 | ||
87 | /* XXX: deal with exceptions */ | |
88 | id value([self performSelector:selector withObject:object]); | |
89 | ||
90 | NSMethodSignature *signature([self methodSignatureForSelector:selector]); | |
91 | [context removeAllObjects]; | |
92 | if ([signature methodReturnLength] != 0 && value != nil) | |
93 | [context addObject:value]; | |
94 | ||
95 | stopped = true; | |
96 | ||
97 | [self | |
98 | performSelectorOnMainThread:@selector(wb$doNothing) | |
99 | withObject:nil | |
100 | waitUntilDone:NO | |
101 | ]; | |
102 | ||
103 | [pool release]; | |
104 | } | |
105 | ||
106 | - (id) wb$yieldToSelector:(SEL)selector withObject:(id)object { | |
107 | /*return [self performSelector:selector withObject:object];*/ | |
108 | ||
109 | volatile bool stopped(false); | |
110 | ||
111 | NSMutableArray *context([NSMutableArray arrayWithObjects: | |
112 | [NSValue valueWithPointer:selector], | |
113 | [NSValue valueWithNonretainedObject:object], | |
114 | [NSValue valueWithPointer:const_cast<bool *>(&stopped)], | |
115 | nil]); | |
116 | ||
117 | NSThread *thread([[[NSThread alloc] | |
118 | initWithTarget:self | |
119 | selector:@selector(wb$_yieldToContext:) | |
120 | object:context | |
121 | ] autorelease]); | |
122 | ||
123 | [thread start]; | |
124 | ||
125 | NSRunLoop *loop([NSRunLoop currentRunLoop]); | |
126 | NSDate *future([NSDate distantFuture]); | |
127 | ||
128 | while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); | |
129 | ||
130 | return [context count] == 0 ? nil : [context objectAtIndex:0]; | |
131 | } | |
132 | ||
133 | - (id) wb$yieldToSelector:(SEL)selector { | |
134 | return [self wb$yieldToSelector:selector withObject:nil]; | |
135 | } | |
136 | ||
137 | @end | |
138 | /* }}} */ | |
139 | ||
140 | /* Theme Settings Controller {{{ */ | |
141 | @interface WBSThemesTableViewCell : UITableViewCell { | |
142 | } | |
143 | ||
144 | @end | |
145 | ||
146 | @implementation WBSThemesTableViewCell | |
147 | ||
148 | - (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuse { | |
149 | if ((self = [super initWithFrame:frame reuseIdentifier:reuse]) != nil) { | |
150 | } return self; | |
151 | } | |
152 | ||
153 | - (void) setTheme:(NSDictionary *)theme { | |
154 | self.text = [theme objectForKey:@"Name"]; | |
155 | self.hidesAccessoryWhenEditing = NO; | |
156 | NSNumber *active([theme objectForKey:@"Active"]); | |
157 | BOOL inactive(active == nil || ![active boolValue]); | |
158 | [self setImage:(inactive ? uncheckedImage : checkImage)]; | |
159 | } | |
160 | ||
161 | @end | |
162 | ||
163 | @interface WBSThemesController: PSViewController <UITableViewDelegate, UITableViewDataSource> { | |
164 | UITableView *_tableView; | |
165 | NSMutableArray *_themes; | |
166 | } | |
167 | ||
168 | @property (nonatomic, retain) NSMutableArray *themes; | |
169 | ||
170 | - (id) initForContentSize:(CGSize)size; | |
171 | - (id) view; | |
172 | - (id) navigationTitle; | |
173 | - (void) themesChanged; | |
174 | ||
175 | - (int) numberOfSectionsInTableView:(UITableView *)tableView; | |
176 | - (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section; | |
177 | - (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section; | |
178 | - (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; | |
179 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; | |
180 | - (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath; | |
181 | - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath; | |
182 | - (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath; | |
183 | - (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath; | |
184 | ||
185 | @end | |
186 | ||
187 | @implementation WBSThemesController | |
188 | ||
189 | @synthesize themes = _themes; | |
190 | ||
191 | + (void) initialize { | |
192 | NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); | |
193 | checkImage = [_UIImageWithName(@"UIPreferencesBlueCheck.png") retain]; | |
194 | uncheckedImage = [[UIImage imageWithContentsOfFile:@"/System/Library/PreferenceBundles/WinterBoardSettings.bundle/SearchResultsCheckmarkClear.png"] retain]; | |
195 | [pool release]; | |
196 | } | |
197 | ||
198 | - (id) initForContentSize:(CGSize)size { | |
199 | if ((self = [super initForContentSize:size]) != nil) { | |
200 | self.themes = [_settings objectForKey:@"Themes"]; | |
201 | if (!_themes) { | |
202 | if (NSString *theme = [_settings objectForKey:@"Theme"]) { | |
203 | self.themes = [NSMutableArray arrayWithObject: | |
204 | [NSMutableDictionary dictionaryWithObjectsAndKeys: | |
205 | theme, @"Name", | |
206 | [NSNumber numberWithBool:YES], @"Active", nil]]; | |
207 | [_settings removeObjectForKey:@"Theme"]; | |
208 | } | |
209 | if (!_themes) | |
210 | self.themes = [NSMutableArray array]; | |
211 | [_settings setObject:_themes forKey:@"Themes"]; | |
212 | } | |
213 | ||
214 | NSMutableArray *themesOnDisk([NSMutableArray array]); | |
215 | AddThemes(themesOnDisk, @"/Library/Themes"); | |
216 | AddThemes(themesOnDisk, [NSString stringWithFormat:@"%@/Library/SummerBoard/Themes", NSHomeDirectory()]); | |
217 | ||
218 | for (int i = 0, count = [themesOnDisk count]; i < count; i++) { | |
219 | NSString *theme = [themesOnDisk objectAtIndex:i]; | |
220 | if ([theme hasSuffix:@".theme"]) | |
221 | [themesOnDisk replaceObjectAtIndex:i withObject:[theme stringByDeletingPathExtension]]; | |
222 | } | |
223 | ||
224 | NSMutableSet *themesSet([NSMutableSet set]); | |
225 | ||
226 | for (int i = 0, count = [_themes count]; i < count; i++) { | |
227 | NSDictionary *theme([_themes objectAtIndex:i]); | |
228 | NSString *name([theme objectForKey:@"Name"]); | |
229 | ||
230 | if (!name || ![themesOnDisk containsObject:name]) { | |
231 | [_themes removeObjectAtIndex:i]; | |
232 | i--; | |
233 | count--; | |
234 | } else { | |
235 | [themesSet addObject:name]; | |
236 | } | |
237 | } | |
238 | ||
239 | for (NSString *theme in themesOnDisk) { | |
240 | if ([themesSet containsObject:theme]) | |
241 | continue; | |
242 | [themesSet addObject:theme]; | |
243 | ||
244 | [_themes insertObject:[NSMutableDictionary dictionaryWithObjectsAndKeys: | |
245 | theme, @"Name", | |
246 | [NSNumber numberWithBool:NO], @"Active", | |
247 | nil] atIndex:0]; | |
248 | } | |
249 | ||
250 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 480-64) style:UITableViewStyleGrouped]; | |
251 | [_tableView setDataSource:self]; | |
252 | [_tableView setDelegate:self]; | |
253 | [_tableView setEditing:YES]; | |
254 | [_tableView setAllowsSelectionDuringEditing:YES]; | |
255 | if ([self respondsToSelector:@selector(setView:)]) | |
256 | [self setView:_tableView]; | |
257 | } | |
258 | return self; | |
259 | } | |
260 | ||
261 | - (void) dealloc { | |
262 | [_tableView release]; | |
263 | [_themes release]; | |
264 | [super dealloc]; | |
265 | } | |
266 | ||
267 | - (id) navigationTitle { | |
268 | return @"Themes"; | |
269 | } | |
270 | ||
271 | - (id) view { | |
272 | return _tableView; | |
273 | } | |
274 | ||
275 | - (void) themesChanged { | |
276 | settingsChanged = YES; | |
277 | } | |
278 | ||
279 | /* UITableViewDelegate / UITableViewDataSource Methods {{{ */ | |
280 | - (int) numberOfSectionsInTableView:(UITableView *)tableView { | |
281 | return 1; | |
282 | } | |
283 | ||
284 | - (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section { | |
285 | return nil; | |
286 | } | |
287 | ||
288 | - (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section { | |
289 | return _themes.count; | |
290 | } | |
291 | ||
292 | - (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { | |
293 | WBSThemesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ThemeCell"]; | |
294 | if (!cell) { | |
295 | cell = [[[WBSThemesTableViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100) reuseIdentifier:@"ThemeCell"] autorelease]; | |
296 | //[cell setTableViewStyle:UITableViewCellStyleDefault]; | |
297 | } | |
298 | ||
299 | NSDictionary *theme([_themes objectAtIndex:indexPath.row]); | |
300 | [cell setTheme:theme]; | |
301 | return cell; | |
302 | } | |
303 | ||
304 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { | |
305 | UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; | |
306 | NSMutableDictionary *theme = [_themes objectAtIndex:indexPath.row]; | |
307 | NSNumber *active = [theme objectForKey:@"Active"]; | |
308 | BOOL inactive = active == nil || ![active boolValue]; | |
309 | [theme setObject:[NSNumber numberWithBool:inactive] forKey:@"Active"]; | |
310 | [cell setImage:(!inactive ? uncheckedImage : checkImage)]; | |
311 | [tableView deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:YES]; | |
312 | [self themesChanged]; | |
313 | } | |
314 | ||
315 | - (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { | |
316 | NSUInteger fromIndex = [fromIndexPath row]; | |
317 | NSUInteger toIndex = [toIndexPath row]; | |
318 | if (fromIndex == toIndex) | |
319 | return; | |
320 | NSMutableDictionary *theme = [[[_themes objectAtIndex:fromIndex] retain] autorelease]; | |
321 | [_themes removeObjectAtIndex:fromIndex]; | |
322 | [_themes insertObject:theme atIndex:toIndex]; | |
323 | [self themesChanged]; | |
324 | } | |
325 | ||
326 | - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { | |
327 | return UITableViewCellEditingStyleNone; | |
328 | } | |
329 | ||
330 | - (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath { | |
331 | return NO; | |
332 | } | |
333 | ||
334 | - (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { | |
335 | return YES; | |
336 | } | |
337 | /* }}} */ | |
338 | @end | |
339 | /* }}} */ | |
340 | ||
341 | @interface WBAdvancedController: PSListController { | |
342 | } | |
343 | ||
344 | - (id) specifiers; | |
345 | - (void) settingsChanged; | |
346 | ||
347 | @end | |
348 | ||
349 | @implementation WBAdvancedController | |
350 | ||
351 | - (id) specifiers { | |
352 | if (!_specifiers) | |
353 | _specifiers = [[self loadSpecifiersFromPlistName:@"Advanced" target:self] retain]; | |
354 | return _specifiers; | |
355 | } | |
356 | ||
357 | - (void) settingsChanged { | |
358 | settingsChanged = YES; | |
359 | } | |
360 | ||
361 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec { | |
362 | NSString *key([spec propertyForKey:@"key"]); | |
363 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
364 | value = [NSNumber numberWithBool:(![value boolValue])]; | |
365 | [_settings setValue:value forKey:key]; | |
366 | [self settingsChanged]; | |
367 | } | |
368 | ||
369 | - (id) readPreferenceValue:(PSSpecifier *)spec { | |
370 | NSString *key([spec propertyForKey:@"key"]); | |
371 | id defaultValue([spec propertyForKey:@"default"]); | |
372 | id plistValue([_settings objectForKey:key]); | |
373 | if (!plistValue) | |
374 | return defaultValue; | |
375 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
376 | plistValue = [NSNumber numberWithBool:(![plistValue boolValue])]; | |
377 | return plistValue; | |
378 | } | |
379 | ||
380 | - (void) __optimizeThemes { | |
381 | system("/usr/libexec/winterboard/Optimize"); | |
382 | } | |
383 | ||
384 | - (void) optimizeThemes { | |
385 | UIAlertView *alert([[[UIAlertView alloc] | |
386 | initWithTitle:@"Optimize Themes" | |
387 | message:@"Please note that this setting /replaces/ the PNG files that came with the theme. PNG files that have been iPhone-optimized cannot be viewed on a normal computer unless they are first deoptimized. You can use Cydia to reinstall themes that have been optimized in order to revert to the original PNG files." | |
388 | delegate:self | |
389 | cancelButtonTitle:@"Cancel" | |
390 | otherButtonTitles:@"Optimize", nil | |
391 | ] autorelease]); | |
392 | ||
393 | [alert setContext:@"optimize"]; | |
394 | [alert setNumberOfRows:1]; | |
395 | [alert show]; | |
396 | } | |
397 | ||
398 | - (void) _optimizeThemes { | |
399 | UIView *view([self view]); | |
400 | UIWindow *window([view window]); | |
401 | ||
402 | UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window] autorelease]); | |
403 | [hud setText:@"Reticulating Splines\nPlease Wait (Minutes)"]; | |
404 | ||
405 | [window setUserInteractionEnabled:NO]; | |
406 | ||
407 | [window addSubview:hud]; | |
408 | [hud show:YES]; | |
409 | [self wb$yieldToSelector:@selector(__optimizeThemes)]; | |
410 | [hud removeFromSuperview]; | |
411 | ||
412 | [window setUserInteractionEnabled:YES]; | |
413 | ||
414 | [self settingsChanged]; | |
415 | } | |
416 | ||
417 | - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { | |
418 | NSString *context([alert context]); | |
419 | ||
420 | if ([context isEqualToString:@"optimize"]) { | |
421 | if (button == [alert firstOtherButtonIndex]) { | |
422 | [self performSelector:@selector(_optimizeThemes) withObject:nil afterDelay:0]; | |
423 | } | |
424 | ||
425 | [alert dismissWithClickedButtonIndex:-1 animated:YES]; | |
426 | } | |
427 | /*else if ([super respondsToSelector:@selector(alertView:clickedButtonAtIndex:)]) | |
428 | [super alertView:alert clickedButtonAtIndex:button];*/ | |
429 | } | |
430 | ||
431 | @end | |
432 | ||
433 | @interface WBSettingsController: PSListController { | |
434 | } | |
435 | ||
436 | - (id) initForContentSize:(CGSize)size; | |
437 | - (void) dealloc; | |
438 | - (void) suspend; | |
439 | - (void) navigationBarButtonClicked:(int)buttonIndex; | |
440 | - (void) viewWillRedisplay; | |
441 | - (void) pushController:(id)controller; | |
442 | - (id) specifiers; | |
443 | - (void) settingsChanged; | |
444 | - (NSString *) title; | |
445 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec; | |
446 | - (id) readPreferenceValue:(PSSpecifier *)spec; | |
447 | ||
448 | @end | |
449 | ||
450 | @implementation WBSettingsController | |
451 | ||
452 | - (void) _wb$loadSettings { | |
453 | _plist = [[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.WinterBoard.plist", NSHomeDirectory()] retain]; | |
454 | _settings = [NSMutableDictionary dictionaryWithContentsOfFile:_plist]; | |
455 | ||
456 | bool set; | |
457 | if (_settings != nil) | |
458 | set = true; | |
459 | else { | |
460 | set = false; | |
461 | _settings = [NSMutableDictionary dictionary]; | |
462 | } | |
463 | ||
464 | _settings = [_settings retain]; | |
465 | ||
466 | if ([_settings objectForKey:@"SummerBoard"] == nil) | |
467 | [_settings setObject:[NSNumber numberWithBool:set] forKey:@"SummerBoard"]; | |
468 | } | |
469 | ||
470 | - (id) initForContentSize:(CGSize)size { | |
471 | if ((self = [super initForContentSize:size]) != nil) { | |
472 | [self _wb$loadSettings]; | |
473 | } return self; | |
474 | } | |
475 | ||
476 | - (void) dealloc { | |
477 | [_settings release]; | |
478 | [_plist release]; | |
479 | [super dealloc]; | |
480 | } | |
481 | ||
482 | - (void) suspend { | |
483 | if (!settingsChanged) | |
484 | return; | |
485 | ||
486 | NSData *data([NSPropertyListSerialization dataFromPropertyList:_settings format:NSPropertyListBinaryFormat_v1_0 errorDescription:NULL]); | |
487 | if (!data) | |
488 | return; | |
489 | if (![data writeToFile:_plist options:NSAtomicWrite error:NULL]) | |
490 | return; | |
491 | ||
492 | unlink("/User/Library/Caches/com.apple.springboard-imagecache-icons"); | |
493 | unlink("/User/Library/Caches/com.apple.springboard-imagecache-icons.plist"); | |
494 | unlink("/User/Library/Caches/com.apple.springboard-imagecache-smallicons"); | |
495 | unlink("/User/Library/Caches/com.apple.springboard-imagecache-smallicons.plist"); | |
496 | ||
497 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen"); | |
498 | unlink("/User/Library/Caches/com.apple.SpringBoard.notificationCenterLinen"); | |
499 | ||
500 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.0"); | |
501 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.1"); | |
502 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.2"); | |
503 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.3"); | |
504 | ||
505 | system("rm -rf /User/Library/Caches/SpringBoardIconCache"); | |
506 | system("rm -rf /User/Library/Caches/SpringBoardIconCache-small"); | |
507 | system("rm -rf /User/Library/Caches/com.apple.IconsCache"); | |
508 | system("rm -rf /User/Library/Caches/com.apple.newsstand"); | |
509 | system("rm -rf /User/Library/Caches/com.apple.springboard.sharedimagecache"); | |
510 | system("rm -rf /User/Library/Caches/com.apple.UIStatusBar"); | |
511 | ||
512 | system("rm -rf /User/Library/Caches/BarDialer"); | |
513 | system("rm -rf /User/Library/Caches/BarDialer_selected"); | |
514 | system("rm -rf /User/Library/Caches/BarRecents"); | |
515 | system("rm -rf /User/Library/Caches/BarRecents_selected"); | |
516 | system("rm -rf /User/Library/Caches/BarVM"); | |
517 | system("rm -rf /User/Library/Caches/BarVM_selected"); | |
518 | ||
519 | system("killall -9 lsd"); | |
520 | ||
521 | if (kCFCoreFoundationVersionNumber > 700) // XXX: iOS 6.x | |
522 | system("killall backboardd"); | |
523 | else | |
524 | system("killall SpringBoard"); | |
525 | } | |
526 | ||
527 | - (void) cancelChanges { | |
528 | [_settings release]; | |
529 | [_plist release]; | |
530 | ||
531 | [self _wb$loadSettings]; | |
532 | ||
533 | [self reloadSpecifiers]; | |
534 | if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) { | |
535 | [[self navigationItem] setLeftBarButtonItem:nil]; | |
536 | [[self navigationItem] setRightBarButtonItem:nil]; | |
537 | } else { | |
538 | [self showLeftButton:nil withStyle:0 rightButton:nil withStyle:0]; | |
539 | } | |
540 | settingsChanged = NO; | |
541 | } | |
542 | ||
543 | - (void) navigationBarButtonClicked:(int)buttonIndex { | |
544 | if (!settingsChanged) { | |
545 | [super navigationBarButtonClicked:buttonIndex]; | |
546 | return; | |
547 | } | |
548 | ||
549 | if (buttonIndex == 0) { | |
550 | [self cancelChanges]; | |
551 | return; | |
552 | } | |
553 | ||
554 | [self suspend]; | |
555 | [self.rootController popController]; | |
556 | } | |
557 | ||
558 | - (void) settingsConfirmButtonClicked:(UIBarButtonItem *)button { | |
559 | [self navigationBarButtonClicked:button.tag]; | |
560 | } | |
561 | ||
562 | - (void) viewWillRedisplay { | |
563 | if (settingsChanged) | |
564 | [self settingsChanged]; | |
565 | [super viewWillRedisplay]; | |
566 | } | |
567 | ||
568 | - (void) viewWillAppear:(BOOL)animated { | |
569 | if (settingsChanged) | |
570 | [self settingsChanged]; | |
571 | if ([super respondsToSelector:@selector(viewWillAppear:)]) | |
572 | [super viewWillAppear:animated]; | |
573 | } | |
574 | ||
575 | - (void) pushController:(id)controller { | |
576 | [self hideNavigationBarButtons]; | |
577 | [super pushController:controller]; | |
578 | } | |
579 | ||
580 | - (id) specifiers { | |
581 | if (!_specifiers) { | |
582 | NSMutableArray *specifiers([NSMutableArray array]); | |
583 | for (PSSpecifier *specifier in [self loadSpecifiersFromPlistName:@"WinterBoard" target:self]) { | |
584 | if (NSArray *version = [specifier propertyForKey:@"wb$filter"]) { | |
585 | size_t count([version count]); | |
586 | if (count == 0 || count > 2) | |
587 | continue; | |
588 | ||
589 | double lower([[version objectAtIndex:0] doubleValue]); | |
590 | if (kCFCoreFoundationVersionNumber < lower) | |
591 | continue; | |
592 | ||
593 | if (count != 1) { | |
594 | double upper([[version objectAtIndex:1] doubleValue]); | |
595 | if (upper <= kCFCoreFoundationVersionNumber) | |
596 | continue; | |
597 | } | |
598 | } | |
599 | [specifiers addObject:specifier]; | |
600 | } | |
601 | _specifiers = [specifiers retain]; | |
602 | } | |
603 | return _specifiers; | |
604 | } | |
605 | ||
606 | - (void) settingsChanged { | |
607 | if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) { | |
608 | UIBarButtonItem *respringButton([[UIBarButtonItem alloc] initWithTitle:@"Respring" style:UIBarButtonItemStyleDone target:self action:@selector(settingsConfirmButtonClicked:)]); | |
609 | UIBarButtonItem *cancelButton([[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(settingsConfirmButtonClicked:)]); | |
610 | cancelButton.tag = 0; | |
611 | respringButton.tag = 1; | |
612 | [[self navigationItem] setLeftBarButtonItem:respringButton]; | |
613 | [[self navigationItem] setRightBarButtonItem:cancelButton]; | |
614 | [respringButton release]; | |
615 | [cancelButton release]; | |
616 | } else { | |
617 | [self showLeftButton:@"Respring" withStyle:2 rightButton:@"Cancel" withStyle:0]; | |
618 | } | |
619 | settingsChanged = YES; | |
620 | } | |
621 | ||
622 | - (NSString *) title { | |
623 | return @"WinterBoard"; | |
624 | } | |
625 | ||
626 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec { | |
627 | NSString *key([spec propertyForKey:@"key"]); | |
628 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
629 | value = [NSNumber numberWithBool:(![value boolValue])]; | |
630 | [_settings setValue:value forKey:key]; | |
631 | [self settingsChanged]; | |
632 | } | |
633 | ||
634 | - (id) readPreferenceValue:(PSSpecifier *)spec { | |
635 | NSString *key([spec propertyForKey:@"key"]); | |
636 | id defaultValue([spec propertyForKey:@"default"]); | |
637 | id plistValue([_settings objectForKey:key]); | |
638 | if (!plistValue) | |
639 | return defaultValue; | |
640 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
641 | plistValue = [NSNumber numberWithBool:(![plistValue boolValue])]; | |
642 | return plistValue; | |
643 | } | |
644 | ||
645 | @end | |
646 | ||
647 | #define WBSAddMethod(_class, _sel, _imp, _type) \ | |
648 | if (![[_class class] instancesRespondToSelector:@selector(_sel)]) \ | |
649 | class_addMethod([_class class], @selector(_sel), (IMP)_imp, _type) | |
650 | void $PSRootController$popController(PSRootController *self, SEL _cmd) { | |
651 | [self popViewControllerAnimated:YES]; | |
652 | } | |
653 | ||
654 | void $PSViewController$hideNavigationBarButtons(PSRootController *self, SEL _cmd) { | |
655 | } | |
656 | ||
657 | id $PSViewController$initForContentSize$(PSRootController *self, SEL _cmd, CGRect contentSize) { | |
658 | return [self init]; | |
659 | } | |
660 | ||
661 | static __attribute__((constructor)) void __wbsInit() { | |
662 | WBSAddMethod(PSRootController, popController, $PSRootController$popController, "v@:"); | |
663 | WBSAddMethod(PSViewController, hideNavigationBarButtons, $PSViewController$hideNavigationBarButtons, "v@:"); | |
664 | WBSAddMethod(PSViewController, initForContentSize:, $PSViewController$initForContentSize$, "@@:{ff}"); | |
665 | } |