]>
Commit | Line | Data |
---|---|---|
36228fbc | 1 | /* CydgetScript - open-source lock screen multiplexer |
5bc15b17 | 2 | * Copyright (C) 2009-2014 Jay Freeman (saurik) |
545370d8 JF |
3 | */ |
4 | ||
5bc15b17 | 5 | /* GNU General Public License, Version 3 {{{ */ |
545370d8 | 6 | /* |
5bc15b17 JF |
7 | * Cydia is free software: you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published | |
9 | * by the Free Software Foundation, either version 3 of the License, | |
10 | * or (at your option) any later version. | |
545370d8 | 11 | * |
5bc15b17 JF |
12 | * Cydia is distributed in the hope that it will be useful, but |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
545370d8 | 16 | * |
5bc15b17 JF |
17 | * You should have received a copy of the GNU General Public License |
18 | * along with Cydia. If not, see <http://www.gnu.org/licenses/>. | |
19 | **/ | |
20 | /* }}} */ | |
545370d8 JF |
21 | |
22 | #import <Foundation/Foundation.h> | |
23 | #import <UIKit/UIKit.h> | |
2140e557 JF |
24 | #import <Preferences/PSRootController.h> |
25 | #import <Preferences/PSViewController.h> | |
545370d8 JF |
26 | #import <Preferences/PSListController.h> |
27 | #import <Preferences/PSSpecifier.h> | |
28 | #import <Preferences/PSTableCell.h> | |
29 | #import <UIKit/UINavigationButton.h> | |
30 | ||
2140e557 JF |
31 | #include <dlfcn.h> |
32 | #include <objc/runtime.h> | |
33 | ||
545370d8 JF |
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 | /* Theme Settings Controller {{{ */ | |
3a0081b3 | 45 | @interface CydgetOrderController: PSViewController <UITableViewDelegate, UITableViewDataSource> { |
545370d8 JF |
46 | UITableView *_tableView; |
47 | NSMutableArray *_themes; | |
48 | } | |
49 | ||
50 | @property (nonatomic, retain) NSMutableArray *themes; | |
51 | ||
52 | + (void) load; | |
53 | ||
54 | - (id) initForContentSize:(CGSize)size; | |
55 | - (id) view; | |
56 | - (id) navigationTitle; | |
57 | - (void) themesChanged; | |
58 | ||
59 | - (int) numberOfSectionsInTableView:(UITableView *)tableView; | |
60 | - (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section; | |
61 | - (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section; | |
62 | - (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; | |
63 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; | |
64 | - (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath; | |
65 | - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath; | |
66 | - (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath; | |
67 | - (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath; | |
68 | ||
69 | @end | |
70 | ||
3a0081b3 | 71 | @implementation CydgetOrderController |
545370d8 JF |
72 | |
73 | @synthesize themes = _themes; | |
74 | ||
75 | + (void) load { | |
76 | NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); | |
77 | checkImage = [_UIImageWithName(@"UIPreferencesBlueCheck.png") retain]; | |
3a0081b3 | 78 | uncheckedImage = [[UIImage imageWithContentsOfFile:@"/System/Library/PreferenceBundles/CydgetSettings.bundle/SearchResultsCheckmarkClear.png"] retain]; |
545370d8 JF |
79 | [pool release]; |
80 | } | |
81 | ||
82 | - (id) initForContentSize:(CGSize)size { | |
83 | if ((self = [super initForContentSize:size]) != nil) { | |
3a0081b3 | 84 | self.themes = [_settings objectForKey:@"LockCydgets"]; |
545370d8 | 85 | if (!_themes) { |
3a0081b3 | 86 | self.themes = [NSMutableArray arrayWithObjects:[NSMutableDictionary dictionaryWithObjectsAndKeys: |
3aa346e5 | 87 | @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil |
3a0081b3 | 88 | ], [NSMutableDictionary dictionaryWithObjectsAndKeys: |
3aa346e5 | 89 | @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil |
3a0081b3 | 90 | ], nil]; |
545370d8 | 91 | |
3a0081b3 | 92 | [_settings setObject:_themes forKey:@"LockCydgets"]; |
545370d8 JF |
93 | } |
94 | ||
3a0081b3 JF |
95 | NSMutableArray *themesOnDisk([NSMutableArray arrayWithCapacity:4]); |
96 | for (NSString *theme in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/System/Library/LockCydgets" error:NULL]) | |
97 | if ([theme hasSuffix:@".cydget"]) | |
ad12f828 JF |
98 | if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"/System/Library/LockCydgets/%@/Info.plist", theme]]) { |
99 | if (NSArray *version = [info objectForKey:@"CYVersionFilter"]) { | |
100 | size_t count([version count]); | |
101 | if (count == 0 || count > 2) | |
102 | continue; | |
103 | ||
104 | double lower([[version objectAtIndex:0] doubleValue]); | |
105 | if (kCFCoreFoundationVersionNumber < lower) | |
106 | continue; | |
107 | ||
108 | if (count != 1) { | |
109 | double upper([[version objectAtIndex:1] doubleValue]); | |
110 | if (upper <= kCFCoreFoundationVersionNumber) | |
111 | continue; | |
112 | } | |
113 | } | |
114 | ||
115 | [themesOnDisk addObject:[theme stringByDeletingPathExtension]]; | |
116 | } | |
3a0081b3 | 117 | |
545370d8 JF |
118 | NSMutableSet *themesSet([NSMutableSet set]); |
119 | ||
120 | for (int i = 0, count = [_themes count]; i < count; i++) { | |
121 | NSDictionary *theme([_themes objectAtIndex:i]); | |
122 | NSString *name([theme objectForKey:@"Name"]); | |
123 | ||
124 | if (!name || ![themesOnDisk containsObject:name]) { | |
125 | [_themes removeObjectAtIndex:i]; | |
126 | i--; | |
127 | count--; | |
128 | } else { | |
129 | [themesSet addObject:name]; | |
130 | } | |
131 | } | |
132 | ||
133 | for (NSString *theme in themesOnDisk) { | |
134 | if ([themesSet containsObject:theme]) | |
135 | continue; | |
136 | [themesSet addObject:theme]; | |
137 | ||
138 | [_themes insertObject:[NSMutableDictionary dictionaryWithObjectsAndKeys: | |
139 | theme, @"Name", | |
140 | [NSNumber numberWithBool:NO], @"Active", | |
141 | nil] atIndex:0]; | |
142 | } | |
143 | ||
144 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 480-64) style:UITableViewStyleGrouped]; | |
145 | [_tableView setDataSource:self]; | |
146 | [_tableView setDelegate:self]; | |
147 | [_tableView setEditing:YES]; | |
148 | [_tableView setAllowsSelectionDuringEditing:YES]; | |
2140e557 JF |
149 | if ([self respondsToSelector:@selector(setView:)]) |
150 | [self setView:_tableView]; | |
545370d8 JF |
151 | } |
152 | return self; | |
153 | } | |
154 | ||
155 | - (void) dealloc { | |
156 | [_tableView release]; | |
157 | [_themes release]; | |
158 | [super dealloc]; | |
159 | } | |
160 | ||
161 | - (id) navigationTitle { | |
3a0081b3 | 162 | return @"Lock Cydgets"; |
545370d8 JF |
163 | } |
164 | ||
165 | - (id) view { | |
166 | return _tableView; | |
167 | } | |
168 | ||
169 | - (void) themesChanged { | |
170 | settingsChanged = YES; | |
171 | } | |
172 | ||
173 | /* UITableViewDelegate / UITableViewDataSource Methods {{{ */ | |
174 | - (int) numberOfSectionsInTableView:(UITableView *)tableView { | |
175 | return 1; | |
176 | } | |
177 | ||
178 | - (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section { | |
179 | return nil; | |
180 | } | |
181 | ||
182 | - (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section { | |
183 | return _themes.count; | |
184 | } | |
185 | ||
186 | - (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { | |
187 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ThemeCell"]; | |
188 | if (!cell) { | |
189 | cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100) reuseIdentifier:@"ThemeCell"] autorelease]; | |
190 | //[cell setTableViewStyle:UITableViewCellStyleDefault]; | |
191 | } | |
192 | ||
193 | NSDictionary *theme([_themes objectAtIndex:indexPath.row]); | |
194 | cell.text = [theme objectForKey:@"Name"]; | |
195 | cell.hidesAccessoryWhenEditing = NO; | |
196 | NSNumber *active([theme objectForKey:@"Active"]); | |
197 | BOOL inactive(active == nil || ![active boolValue]); | |
198 | [cell setImage:(inactive ? uncheckedImage : checkImage)]; | |
199 | return cell; | |
200 | } | |
201 | ||
202 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { | |
203 | UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; | |
204 | NSMutableDictionary *theme = [_themes objectAtIndex:indexPath.row]; | |
205 | NSNumber *active = [theme objectForKey:@"Active"]; | |
206 | BOOL inactive = active == nil || ![active boolValue]; | |
207 | [theme setObject:[NSNumber numberWithBool:inactive] forKey:@"Active"]; | |
208 | [cell setImage:(!inactive ? uncheckedImage : checkImage)]; | |
209 | [tableView deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:YES]; | |
210 | [self themesChanged]; | |
211 | } | |
212 | ||
213 | - (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { | |
214 | NSUInteger fromIndex = [fromIndexPath row]; | |
215 | NSUInteger toIndex = [toIndexPath row]; | |
216 | if (fromIndex == toIndex) | |
217 | return; | |
218 | NSMutableDictionary *theme = [[[_themes objectAtIndex:fromIndex] retain] autorelease]; | |
219 | [_themes removeObjectAtIndex:fromIndex]; | |
220 | [_themes insertObject:theme atIndex:toIndex]; | |
221 | [self themesChanged]; | |
222 | } | |
223 | ||
224 | - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { | |
225 | return UITableViewCellEditingStyleNone; | |
226 | } | |
227 | ||
228 | - (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath { | |
229 | return NO; | |
230 | } | |
231 | ||
232 | - (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { | |
233 | return YES; | |
234 | } | |
235 | /* }}} */ | |
236 | @end | |
237 | /* }}} */ | |
238 | ||
3a0081b3 | 239 | @interface CydgetSettingsController: PSListController { |
545370d8 JF |
240 | } |
241 | ||
242 | - (id) initForContentSize:(CGSize)size; | |
243 | - (void) dealloc; | |
244 | - (void) suspend; | |
245 | - (void) navigationBarButtonClicked:(int)buttonIndex; | |
246 | - (void) viewWillRedisplay; | |
247 | - (void) pushController:(id)controller; | |
248 | - (id) specifiers; | |
249 | - (void) settingsChanged; | |
250 | - (NSString *) title; | |
251 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec; | |
252 | - (id) readPreferenceValue:(PSSpecifier *)spec; | |
253 | ||
254 | @end | |
255 | ||
3a0081b3 | 256 | @implementation CydgetSettingsController |
545370d8 JF |
257 | |
258 | - (id) initForContentSize:(CGSize)size { | |
259 | if ((self = [super initForContentSize:size]) != nil) { | |
3a0081b3 | 260 | _plist = [[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()] retain]; |
545370d8 JF |
261 | _settings = [([NSMutableDictionary dictionaryWithContentsOfFile:_plist] ?: [NSMutableDictionary dictionary]) retain]; |
262 | } return self; | |
263 | } | |
264 | ||
265 | - (void) dealloc { | |
266 | [_settings release]; | |
267 | [_plist release]; | |
268 | [super dealloc]; | |
269 | } | |
270 | ||
271 | - (void) suspend { | |
272 | if (!settingsChanged) | |
273 | return; | |
274 | ||
275 | NSData *data([NSPropertyListSerialization dataFromPropertyList:_settings format:NSPropertyListBinaryFormat_v1_0 errorDescription:NULL]); | |
276 | if (!data) | |
277 | return; | |
278 | if (![data writeToFile:_plist options:NSAtomicWrite error:NULL]) | |
279 | return; | |
74e83991 JF |
280 | |
281 | if (kCFCoreFoundationVersionNumber >= 700) // XXX: iOS 6.x | |
282 | system("killall backboardd"); | |
283 | else | |
284 | system("killall SpringBoard"); | |
545370d8 JF |
285 | } |
286 | ||
2140e557 JF |
287 | - (void) cancelChanges { |
288 | [_settings release]; | |
289 | [_plist release]; | |
290 | _plist = [[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()] retain]; | |
291 | _settings = [([NSMutableDictionary dictionaryWithContentsOfFile:_plist] ?: [NSMutableDictionary dictionary]) retain]; | |
292 | ||
293 | [self reloadSpecifiers]; | |
294 | if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) { | |
295 | [[self navigationItem] setLeftBarButtonItem:nil]; | |
296 | [[self navigationItem] setRightBarButtonItem:nil]; | |
297 | } else { | |
298 | [self showLeftButton:nil withStyle:0 rightButton:nil withStyle:0]; | |
299 | } | |
300 | settingsChanged = NO; | |
301 | } | |
302 | ||
545370d8 JF |
303 | - (void) navigationBarButtonClicked:(int)buttonIndex { |
304 | if (!settingsChanged) { | |
305 | [super navigationBarButtonClicked:buttonIndex]; | |
306 | return; | |
307 | } | |
308 | ||
2140e557 JF |
309 | if (buttonIndex == 0) { |
310 | [self cancelChanges]; | |
311 | return; | |
312 | } | |
545370d8 JF |
313 | |
314 | [self suspend]; | |
315 | [self.rootController popController]; | |
316 | } | |
317 | ||
2140e557 JF |
318 | - (void) settingsConfirmButtonClicked:(UIBarButtonItem *)button { |
319 | [self navigationBarButtonClicked:button.tag]; | |
320 | } | |
321 | ||
545370d8 JF |
322 | - (void) viewWillRedisplay { |
323 | if (settingsChanged) | |
324 | [self settingsChanged]; | |
325 | [super viewWillRedisplay]; | |
326 | } | |
327 | ||
2140e557 JF |
328 | - (void) viewWillAppear:(BOOL)animated { |
329 | if (settingsChanged) | |
330 | [self settingsChanged]; | |
331 | if ([super respondsToSelector:@selector(viewWillAppear:)]) | |
332 | [super viewWillAppear:animated]; | |
333 | } | |
334 | ||
545370d8 JF |
335 | - (void) pushController:(id)controller { |
336 | [self hideNavigationBarButtons]; | |
337 | [super pushController:controller]; | |
338 | } | |
339 | ||
340 | - (id) specifiers { | |
341 | if (!_specifiers) | |
3a0081b3 | 342 | _specifiers = [[self loadSpecifiersFromPlistName:@"Cydget" target:self] retain]; |
545370d8 JF |
343 | return _specifiers; |
344 | } | |
345 | ||
346 | - (void) settingsChanged { | |
2140e557 JF |
347 | if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) { |
348 | UIBarButtonItem *respringButton([[UIBarButtonItem alloc] initWithTitle:@"Respring" style:UIBarButtonItemStyleDone target:self action:@selector(settingsConfirmButtonClicked:)]); | |
349 | UIBarButtonItem *cancelButton([[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(settingsConfirmButtonClicked:)]); | |
350 | cancelButton.tag = 0; | |
351 | respringButton.tag = 1; | |
352 | [[self navigationItem] setLeftBarButtonItem:respringButton]; | |
353 | [[self navigationItem] setRightBarButtonItem:cancelButton]; | |
354 | [respringButton release]; | |
355 | [cancelButton release]; | |
356 | } else { | |
357 | [self showLeftButton:@"Respring" withStyle:2 rightButton:@"Cancel" withStyle:0]; | |
358 | } | |
545370d8 JF |
359 | settingsChanged = YES; |
360 | } | |
361 | ||
362 | - (NSString *) title { | |
3a0081b3 | 363 | return @"Cydget"; |
545370d8 JF |
364 | } |
365 | ||
366 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec { | |
2140e557 | 367 | NSString *key([spec propertyForKey:@"key"]); |
545370d8 JF |
368 | if ([[spec propertyForKey:@"negate"] boolValue]) |
369 | value = [NSNumber numberWithBool:(![value boolValue])]; | |
2140e557 | 370 | [_settings setValue:value forKey:key]; |
545370d8 JF |
371 | [self settingsChanged]; |
372 | } | |
373 | ||
374 | - (id) readPreferenceValue:(PSSpecifier *)spec { | |
375 | NSString *key([spec propertyForKey:@"key"]); | |
376 | id defaultValue([spec propertyForKey:@"default"]); | |
377 | id plistValue([_settings objectForKey:key]); | |
378 | if (!plistValue) | |
379 | return defaultValue; | |
380 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
381 | plistValue = [NSNumber numberWithBool:(![plistValue boolValue])]; | |
382 | return plistValue; | |
383 | } | |
384 | ||
385 | @end | |
2140e557 JF |
386 | |
387 | #define WBSAddMethod(_class, _sel, _imp, _type) \ | |
388 | if (![[_class class] instancesRespondToSelector:@selector(_sel)]) \ | |
389 | class_addMethod([_class class], @selector(_sel), (IMP)_imp, _type) | |
390 | void $PSRootController$popController(PSRootController *self, SEL _cmd) { | |
391 | [self popViewControllerAnimated:YES]; | |
392 | } | |
393 | ||
394 | void $PSViewController$hideNavigationBarButtons(PSRootController *self, SEL _cmd) { | |
395 | } | |
396 | ||
397 | id $PSViewController$initForContentSize$(PSRootController *self, SEL _cmd, CGRect contentSize) { | |
398 | return [self init]; | |
399 | } | |
400 | ||
401 | static __attribute__((constructor)) void __wbsInit() { | |
402 | WBSAddMethod(PSRootController, popController, $PSRootController$popController, "v@:"); | |
403 | WBSAddMethod(PSViewController, hideNavigationBarButtons, $PSViewController$hideNavigationBarButtons, "v@:"); | |
404 | WBSAddMethod(PSViewController, initForContentSize:, $PSViewController$initForContentSize$, "@@:{ff}"); | |
405 | } |