]>
Commit | Line | Data |
---|---|---|
36228fbc | 1 | /* Cydget - open-source AwayView plugin multiplexer |
5bc15b17 | 2 | * Copyright (C) 2009-2014 Jay Freeman (saurik) |
e6cd4dee JF |
3 | */ |
4 | ||
5bc15b17 | 5 | /* GNU General Public License, Version 3 {{{ */ |
e6cd4dee | 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. | |
e6cd4dee | 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. | |
e6cd4dee | 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 | /* }}} */ | |
e6cd4dee | 21 | |
5bc15b17 | 22 | #include <CydiaSubstrate/CydiaSubstrate.h> |
e6cd4dee JF |
23 | |
24 | #include <Foundation/Foundation.h> | |
545370d8 | 25 | #include <UIKit/UIKit.h> |
e6cd4dee JF |
26 | |
27 | #import <SpringBoard/SBAwayController.h> | |
d86d4327 | 28 | #import <SpringBoard/SBAwayView.h> |
e6cd4dee | 29 | #import <SpringBoard/SBAwayWindow.h> |
3a0081b3 | 30 | #import <SpringBoard/SpringBoard.h> |
e6cd4dee | 31 | |
0c1e3ef0 JF |
32 | #include "Handle.hpp" |
33 | ||
5bc15b17 JF |
34 | #define _trace() \ |
35 | NSLog(@"_trace(%s:%u)@%s %zd", __FILE__, __LINE__, __FUNCTION__, active_) | |
36 | #define _not(type) \ | |
37 | static_cast<type>(~type()) | |
38 | ||
39 | typedef void *GSEventRef; | |
40 | ||
41 | extern "C" void UIKeyboardEnableAutomaticAppearance(); | |
42 | extern "C" void UIKeyboardDisableAutomaticAppearance(); | |
43 | ||
e6cd4dee | 44 | MSClassHook(SpringBoard) |
fde9eca1 | 45 | |
e6cd4dee JF |
46 | MSClassHook(SBAwayController) |
47 | MSClassHook(SBAwayView) | |
3aa346e5 | 48 | MSClassHook(SBAwayWindow) |
5bc15b17 JF |
49 | MSClassHook(SBLockScreenManager) |
50 | MSClassHook(SBLockScreenView) | |
51 | MSClassHook(SBLockScreenViewController) | |
52 | MSClassHook(SBScreenFadeAnimationController) | |
53 | MSClassHook(SBUserAgent) | |
54 | ||
55 | MSInitialize { | |
56 | if (kCFCoreFoundationVersionNumber < 800) { | |
57 | $SBUserAgent = $SBAwayController; | |
58 | $SBLockScreenManager = $SBAwayController; | |
59 | $SBLockScreenView = $SBAwayView; | |
60 | } | |
61 | } | |
e6cd4dee | 62 | |
7d9e43da JF |
63 | @interface SBLockScreenNowPlayingPluginController : NSObject |
64 | - (BOOL) isNowPlayingPluginActive; | |
65 | @end | |
66 | ||
c6daf1f5 | 67 | @interface SBLockScreenViewController : UIViewController |
7d9e43da JF |
68 | - (SBLockScreenView *) lockScreenView; |
69 | - (BOOL) isShowingMediaControls; | |
c6daf1f5 JF |
70 | - (void) _toggleMediaControls; |
71 | @end | |
72 | ||
7d9e43da JF |
73 | @interface SBLockScreenManager : NSObject |
74 | + (SBLockScreenManager *) sharedInstance; | |
75 | - (SBLockScreenViewController *) lockScreenViewController; | |
76 | @end | |
77 | ||
e6cd4dee | 78 | static bool menu_; |
e6cd4dee | 79 | |
5bc15b17 JF |
80 | static _H<NSDictionary> settings_; |
81 | static _H<NSMutableArray> cydgets_; | |
3a0081b3 | 82 | static size_t active_; |
3aa346e5 | 83 | static unsigned online_; |
7d9e43da JF |
84 | static bool nowplaying_; |
85 | ||
86 | static bool NowPlaying() { | |
87 | if (kCFCoreFoundationVersionNumber < 800) | |
88 | return false; | |
89 | if (!nowplaying_) | |
90 | return false; | |
91 | ||
92 | SBLockScreenViewController *controller([[$SBLockScreenManager sharedInstance] lockScreenViewController]); | |
93 | if (controller != nil) { | |
94 | SBLockScreenNowPlayingPluginController *now(MSHookIvar<SBLockScreenNowPlayingPluginController *>(controller, "_nowPlayingController")); | |
95 | if (now != nil && [now isNowPlayingPluginActive]) | |
96 | return true; | |
97 | } | |
98 | ||
99 | return false; | |
100 | } | |
e6cd4dee | 101 | |
d8165c74 JF |
102 | @interface CydgetController : NSObject { |
103 | } | |
104 | ||
105 | + (NSDictionary *) currentConfiguration; | |
3215ed85 | 106 | + (NSString *) currentPath; |
d8165c74 JF |
107 | |
108 | @end | |
109 | ||
110 | @implementation CydgetController | |
111 | ||
112 | + (NSDictionary *) currentConfiguration { | |
d86d4327 JF |
113 | NSDictionary *cydget([cydgets_ objectAtIndex:active_]); |
114 | return [cydget objectForKey:@"CYConfiguration"] ?: [cydget objectForKey:@"Configuration"]; | |
d8165c74 JF |
115 | } |
116 | ||
3215ed85 JF |
117 | + (NSDictionary *) currentPath { |
118 | NSDictionary *cydget([cydgets_ objectAtIndex:active_]); | |
119 | return [cydget objectForKey:@"CYPath"]; | |
120 | } | |
121 | ||
d8165c74 JF |
122 | @end |
123 | ||
5bc15b17 JF |
124 | @interface SBUserAgent : NSObject |
125 | + (SBUserAgent *) sharedUserAgent; | |
126 | - (void) enableLockScreenBundleNamed:(NSString *)bundle activationContext:(id)context; | |
127 | - (void) disableLockScreenBundleNamed:(NSString *)bundle deactivationContext:(id)context; | |
e6cd4dee JF |
128 | @end |
129 | ||
57df9ddb JF |
130 | @interface UIPeripheralHost : NSObject |
131 | + (UIPeripheralHost *) sharedInstance; | |
132 | + (void) _releaseSharedInstance; | |
57df9ddb JF |
133 | @end |
134 | ||
135 | MSClassHook(UIPeripheralHost) | |
136 | ||
137 | @interface UITextEffectsWindow : UIWindow | |
138 | + (UIWindow *) sharedTextEffectsWindow; | |
139 | @end | |
140 | ||
5bc15b17 JF |
141 | @implementation NSDictionary (CydgetLoader) |
142 | ||
143 | - (NSString *) cydget { | |
144 | return [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"]; | |
145 | } | |
e6cd4dee | 146 | |
5bc15b17 JF |
147 | - (void) enableCydget { |
148 | if (NSString *plugin = [self cydget]) { | |
fde9eca1 | 149 | ++online_; |
fde9eca1 | 150 | |
1dad64a0 JF |
151 | if (kCFCoreFoundationVersionNumber < 600) |
152 | UIKeyboardEnableAutomaticAppearance(); | |
57df9ddb | 153 | |
1dad64a0 | 154 | [[UITextEffectsWindow sharedTextEffectsWindow] setWindowLevel:1000]; |
5bc15b17 JF |
155 | |
156 | if (kCFCoreFoundationVersionNumber < 800) | |
157 | [[$SBAwayController sharedAwayController] enableLockScreenBundleWithName:plugin]; | |
158 | else | |
159 | [[$SBUserAgent sharedUserAgent] enableLockScreenBundleNamed:plugin activationContext:nil]; | |
3aa346e5 | 160 | } |
e6cd4dee JF |
161 | } |
162 | ||
5bc15b17 JF |
163 | - (void) disableCydget { |
164 | if (NSString *plugin = [self cydget]) { | |
165 | if (kCFCoreFoundationVersionNumber < 800) | |
166 | [[$SBAwayController sharedAwayController] disableLockScreenBundleWithName:plugin]; | |
167 | else | |
168 | [[$SBUserAgent sharedUserAgent] disableLockScreenBundleNamed:plugin deactivationContext:nil]; | |
169 | ||
57df9ddb | 170 | [$UIPeripheralHost _releaseSharedInstance]; |
fde9eca1 | 171 | |
1dad64a0 JF |
172 | if (kCFCoreFoundationVersionNumber < 600) |
173 | UIKeyboardDisableAutomaticAppearance(); | |
174 | ||
fde9eca1 | 175 | --online_; |
3aa346e5 | 176 | } |
e6cd4dee JF |
177 | } |
178 | ||
179 | @end | |
180 | ||
fde9eca1 JF |
181 | // avoid rendering a keyboard onto the default SBAwayView while automatic keyboard is online |
182 | MSInstanceMessageHook0(UIView *, SBAwayView, inputView) { | |
d373498d | 183 | // XXX: there is a conceptual error here |
1dad64a0 | 184 | if (online_ == 0 && false || kCFCoreFoundationVersionNumber > 600) |
fde9eca1 JF |
185 | return MSOldCall(); |
186 | ||
187 | return [[[UIView alloc] init] autorelease]; | |
188 | } | |
189 | ||
190 | // by default, keyboard actions are redirected to SBAwayController and press menu button | |
57df9ddb | 191 | MSInstanceMessageHook1(void, SpringBoard, handleKeyEvent, GSEventRef, event) { |
1dad64a0 | 192 | if (online_ == 0 || kCFCoreFoundationVersionNumber > 600) |
fde9eca1 JF |
193 | return MSOldCall(event); |
194 | ||
195 | return MSSuperCall(event); | |
57df9ddb JF |
196 | } |
197 | ||
c6daf1f5 JF |
198 | bool media_; |
199 | ||
200 | MSInstanceMessageHook0(void, SBLockScreenViewController, _toggleMediaControls) { | |
201 | if (!media_) | |
202 | MSOldCall(); | |
203 | } | |
204 | ||
205 | MSInstanceMessageHook0(BOOL, SBLockScreenViewController, handleMenuButtonDoubleTap) { | |
206 | menu_ = false; | |
207 | BOOL value(MSOldCall()); | |
208 | if (kCFCoreFoundationVersionNumber >= 800) | |
209 | [self _toggleMediaControls]; | |
210 | return value; | |
211 | } | |
212 | ||
5bc15b17 | 213 | MSInstanceMessageHook0(BOOL, SBLockScreenManager, handleMenuButtonTap) { |
c6daf1f5 JF |
214 | media_ = true; |
215 | BOOL value(MSOldCall()); | |
216 | media_ = false; | |
217 | ||
218 | if (!value && menu_) { | |
7d9e43da JF |
219 | if (active_ != _not(size_t)) |
220 | [[cydgets_ objectAtIndex:active_] disableCydget]; | |
3a0081b3 | 221 | active_ = (active_ + 1) % [cydgets_ count]; |
5bc15b17 | 222 | [[cydgets_ objectAtIndex:active_] enableCydget]; |
33da59d4 JF |
223 | // XXX: or siri doesn't disappear correctly |
224 | return kCFCoreFoundationVersionNumber >= 800 ? value : YES; | |
1b616711 | 225 | } |
e6cd4dee | 226 | |
c6daf1f5 | 227 | return value; |
e6cd4dee JF |
228 | } |
229 | ||
5bc15b17 JF |
230 | void Activate_() { |
231 | menu_ = false; | |
7d9e43da JF |
232 | if (NowPlaying()) { |
233 | for (active_ = 0; active_ != [cydgets_ count]; ++active_) | |
234 | if ([[[cydgets_ objectAtIndex:active_] objectForKey:@"CYName"] isEqualToString:@"AwayView"]) | |
235 | break; | |
236 | if (active_ == [cydgets_ count]) | |
237 | active_ = _not(size_t); | |
238 | } | |
239 | if (active_ != _not(size_t)) | |
240 | [[cydgets_ objectAtIndex:active_] enableCydget]; | |
5bc15b17 JF |
241 | } |
242 | ||
1a274bdf | 243 | static void Undim_(SBAwayController *self) { |
f83bba74 | 244 | if ([self isDimmed]) { |
5bc15b17 | 245 | Activate_(); |
f83bba74 JF |
246 | [[[self awayView] window] makeKeyWindow]; |
247 | } | |
1a274bdf | 248 | } |
f83bba74 | 249 | |
5bc15b17 JF |
250 | MSInstanceMessageHook1(void, SBLockScreenViewController, startLockScreenFadeInAnimationForSource, int, source) { |
251 | Activate_(); | |
252 | MSOldCall(source); | |
253 | } | |
254 | ||
1a274bdf JF |
255 | MSInstanceMessageHook0(void, SBAwayController, undimScreen) { |
256 | Undim_(self); | |
f83bba74 | 257 | MSOldCall(); |
e6cd4dee JF |
258 | } |
259 | ||
1a274bdf JF |
260 | MSInstanceMessageHook1(void, SBAwayController, undimScreen, BOOL, undim) { |
261 | Undim_(self); | |
262 | MSOldCall(undim); | |
263 | } | |
264 | ||
5bc15b17 | 265 | static void Deactivate_() { |
7d9e43da JF |
266 | if (active_ != _not(size_t)) |
267 | [[cydgets_ objectAtIndex:active_] disableCydget]; | |
3a0081b3 | 268 | active_ = 0; |
e6cd4dee JF |
269 | } |
270 | ||
5bc15b17 JF |
271 | MSHook(void, BKSDisplayServicesSetScreenBlanked, BOOL blanked) { |
272 | if (blanked) | |
273 | Deactivate_(); | |
274 | _BKSDisplayServicesSetScreenBlanked(blanked); | |
275 | } | |
276 | ||
277 | MSInitialize { | |
278 | MSHookFunction("_BKSDisplayServicesSetScreenBlanked", MSHake(BKSDisplayServicesSetScreenBlanked)); | |
279 | } | |
280 | ||
281 | MSInstanceMessageHook1(void, SBUserAgent, dimScreen, BOOL, dim) { | |
282 | Deactivate_(); | |
1b616711 | 283 | MSOldCall(dim); |
e6cd4dee JF |
284 | } |
285 | ||
b5e122ba JF |
286 | MSInstanceMessageHook0(void, SpringBoard, _menuButtonWasHeld) { |
287 | menu_ = false; | |
288 | MSOldCall(); | |
289 | } | |
290 | ||
5bc15b17 JF |
291 | MSInstanceMessageHook1(void, SpringBoard, _menuButtonDown, GSEventRef, event) { |
292 | menu_ = true; | |
293 | MSOldCall(event); | |
294 | } | |
295 | ||
f83bba74 | 296 | MSInstanceMessageHook1(void, SpringBoard, menuButtonDown, GSEventRef, event) { |
e6cd4dee | 297 | menu_ = true; |
3a0081b3 | 298 | MSOldCall(event); |
e6cd4dee JF |
299 | } |
300 | ||
3a0081b3 | 301 | MSInstanceMessageHook1(void, SBAwayView, addGestureRecognizer, id, recognizer) { |
1dad64a0 | 302 | if (online_ == 0 || kCFCoreFoundationVersionNumber > 600) |
fde9eca1 | 303 | return MSOldCall(recognizer); |
545370d8 JF |
304 | } |
305 | ||
3aa346e5 | 306 | MSInstanceMessageHook1(void, SBAwayWindow, sendGSEvent, GSEventRef, event) { |
1dad64a0 | 307 | if (online_ == 0 || kCFCoreFoundationVersionNumber > 600) |
fde9eca1 JF |
308 | return MSOldCall(event); |
309 | ||
310 | return MSSuperCall(event); | |
3aa346e5 JF |
311 | } |
312 | ||
3a0081b3 | 313 | #define Cydgets_ @"/System/Library/LockCydgets" |
545370d8 | 314 | |
3a0081b3 JF |
315 | static void CydgetSetup() { |
316 | NSString *plist([NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()]); | |
317 | settings_ = [NSMutableDictionary dictionaryWithContentsOfFile:plist] ?: [NSMutableDictionary dictionary]; | |
545370d8 | 318 | |
7d9e43da JF |
319 | nowplaying_ = [[settings_ objectForKey:@"NowPlaying"] boolValue]; |
320 | ||
3a0081b3 | 321 | NSArray *cydgets([settings_ objectForKey:@"LockCydgets"] ?: [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys: |
3aa346e5 | 322 | @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil |
3a0081b3 | 323 | ], [NSDictionary dictionaryWithObjectsAndKeys: |
3aa346e5 | 324 | @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil |
3a0081b3 | 325 | ], nil]); |
545370d8 | 326 | |
3a0081b3 JF |
327 | cydgets_ = [NSMutableArray arrayWithCapacity:4]; |
328 | for (NSDictionary *cydget in cydgets) | |
3215ed85 | 329 | if ([[cydget objectForKey:@"Active"] boolValue]) { |
7d9e43da JF |
330 | NSString *name([cydget objectForKey:@"Name"]); |
331 | NSString *path([NSString stringWithFormat:@"%@/%@.cydget/Info.plist", Cydgets_, name]); | |
3215ed85 | 332 | if (NSMutableDictionary *info = [NSMutableDictionary dictionaryWithContentsOfFile:path]) { |
7d9e43da | 333 | [info setObject:name forKey:@"CYName"]; |
3215ed85 | 334 | [info setObject:path forKey:@"CYPath"]; |
3a0081b3 | 335 | [cydgets_ addObject:info]; |
3215ed85 JF |
336 | } |
337 | } | |
81c3ce7d JF |
338 | |
339 | if ([cydgets_ count] == 0) | |
340 | cydgets_ = nil; | |
545370d8 JF |
341 | } |
342 | ||
3a0081b3 | 343 | // XXX: this could happen while it is unlocked |
5bc15b17 | 344 | MSInstanceMessageHook1(id, SBLockScreenView, initWithFrame, CGRect, frame) { |
3a0081b3 JF |
345 | self = MSOldCall(frame); |
346 | CydgetSetup(); | |
7d9e43da | 347 | Activate_(); |
3a0081b3 | 348 | return self; |
545370d8 JF |
349 | } |
350 | ||
3a0081b3 JF |
351 | MSInstanceMessageHook1(void, SBAwayController, _finishedUnlockAttemptWithStatus, BOOL, status) { |
352 | if (status) | |
5bc15b17 | 353 | Deactivate_(); |
3a0081b3 | 354 | MSOldCall(status); |
e6cd4dee | 355 | } |
d86d4327 | 356 | |
672fa884 JF |
357 | // this is called occasionally by -[SBAwayView updateInterface] and takes away our keyboard |
358 | MSInstanceMessageHook0(void, SBAwayView, _fixupFirstResponder) { | |
359 | if (online_ == 0) | |
360 | return MSOldCall(); | |
361 | } | |
362 | ||
d86d4327 JF |
363 | MSInstanceMessageHook0(void, SBAwayView, updateInterface) { |
364 | MSOldCall(); | |
365 | ||
366 | NSDictionary *cydget([cydgets_ objectAtIndex:active_]); | |
367 | ||
368 | NSString *background([cydget objectForKey:@"CYBackground"]); | |
369 | if ([background isEqualToString:@"Wallpaper"]) { | |
370 | MSIvarHook(UIView *, _backgroundView); | |
371 | [_backgroundView setAlpha:1.0f]; | |
372 | } | |
373 | ||
374 | if ([[cydget objectForKey:@"CYShowDateTime"] boolValue]) | |
375 | [self addDateView]; | |
376 | } |