1 /* Cydget - open-source AwayView plugin multiplexer
2 * Copyright (C) 2009-2014 Jay Freeman (saurik)
5 /* GNU General Public License, Version 3 {{{ */
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.
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.
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/>.
22 #include <CydiaSubstrate/CydiaSubstrate.h>
24 #include <Foundation/Foundation.h>
25 #include <UIKit/UIKit.h>
27 #import <SpringBoard/SBAwayController.h>
28 #import <SpringBoard/SBAwayView.h>
29 #import <SpringBoard/SBAwayWindow.h>
30 #import <SpringBoard/SpringBoard.h>
35 NSLog(@"_trace(%s:%u)@%s %zd", __FILE__, __LINE__, __FUNCTION__, active_)
37 static_cast<type>(~type())
39 typedef void *GSEventRef;
41 extern "C" void UIKeyboardEnableAutomaticAppearance();
42 extern "C" void UIKeyboardDisableAutomaticAppearance();
44 MSClassHook(SpringBoard)
46 MSClassHook(SBAwayController)
47 MSClassHook(SBAwayView)
48 MSClassHook(SBAwayWindow)
49 MSClassHook(SBLockScreenManager)
50 MSClassHook(SBLockScreenView)
51 MSClassHook(SBLockScreenViewController)
52 MSClassHook(SBScreenFadeAnimationController)
53 MSClassHook(SBUserAgent)
56 if (kCFCoreFoundationVersionNumber < 800) {
57 $SBUserAgent = $SBAwayController;
58 $SBLockScreenManager = $SBAwayController;
59 $SBLockScreenView = $SBAwayView;
63 @interface SBLockScreenNowPlayingPluginController : NSObject
64 - (BOOL) isNowPlayingPluginActive;
67 @interface SBLockScreenViewController : UIViewController
68 - (SBLockScreenView *) lockScreenView;
69 - (BOOL) isShowingMediaControls;
70 - (void) _toggleMediaControls;
73 @interface SBLockScreenManager : NSObject
74 + (SBLockScreenManager *) sharedInstance;
75 - (SBLockScreenViewController *) lockScreenViewController;
80 static _H<NSDictionary> settings_;
81 static _H<NSMutableArray> cydgets_;
82 static size_t active_;
83 static unsigned online_;
84 static bool nowplaying_;
85 static bool remember_;
87 static bool NowPlaying() {
88 if (kCFCoreFoundationVersionNumber < 800)
93 SBLockScreenViewController *controller([[$SBLockScreenManager sharedInstance] lockScreenViewController]);
94 if (controller != nil) {
95 SBLockScreenNowPlayingPluginController *now(MSHookIvar<SBLockScreenNowPlayingPluginController *>(controller, "_nowPlayingController"));
96 if (now != nil && [now isNowPlayingPluginActive])
103 @interface CydgetController : NSObject {
106 + (NSDictionary *) currentConfiguration;
107 + (NSString *) currentPath;
111 @implementation CydgetController
113 + (NSDictionary *) currentConfiguration {
114 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
115 return [cydget objectForKey:@"CYConfiguration"] ?: [cydget objectForKey:@"Configuration"];
118 + (NSDictionary *) currentPath {
119 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
120 return [cydget objectForKey:@"CYPath"];
125 @interface SBUserAgent : NSObject
126 + (SBUserAgent *) sharedUserAgent;
127 - (void) enableLockScreenBundleNamed:(NSString *)bundle activationContext:(id)context;
128 - (void) disableLockScreenBundleNamed:(NSString *)bundle deactivationContext:(id)context;
131 @interface UIPeripheralHost : NSObject
132 + (UIPeripheralHost *) sharedInstance;
133 + (void) _releaseSharedInstance;
136 MSClassHook(UIPeripheralHost)
138 @interface UITextEffectsWindow : UIWindow
139 + (UIWindow *) sharedTextEffectsWindow;
142 @implementation NSDictionary (CydgetLoader)
144 - (NSString *) cydget {
145 return [self objectForKey:@"CYPlugin"] ?: [self objectForKey:@"Plugin"];
148 - (void) enableCydget {
149 if (NSString *plugin = [self cydget]) {
152 if (kCFCoreFoundationVersionNumber < 600)
153 UIKeyboardEnableAutomaticAppearance();
155 [[UITextEffectsWindow sharedTextEffectsWindow] setWindowLevel:1000];
157 if (kCFCoreFoundationVersionNumber < 800)
158 [[$SBAwayController sharedAwayController] enableLockScreenBundleWithName:plugin];
160 [[$SBUserAgent sharedUserAgent] enableLockScreenBundleNamed:plugin activationContext:nil];
164 - (void) disableCydget {
165 if (NSString *plugin = [self cydget]) {
166 if (kCFCoreFoundationVersionNumber < 800)
167 [[$SBAwayController sharedAwayController] disableLockScreenBundleWithName:plugin];
169 [[$SBUserAgent sharedUserAgent] disableLockScreenBundleNamed:plugin deactivationContext:nil];
171 [$UIPeripheralHost _releaseSharedInstance];
173 if (kCFCoreFoundationVersionNumber < 600)
174 UIKeyboardDisableAutomaticAppearance();
182 // avoid rendering a keyboard onto the default SBAwayView while automatic keyboard is online
183 MSInstanceMessageHook0(UIView *, SBAwayView, inputView) {
184 // XXX: there is a conceptual error here
185 if (online_ == 0 && false || kCFCoreFoundationVersionNumber > 600)
188 return [[[UIView alloc] init] autorelease];
191 // by default, keyboard actions are redirected to SBAwayController and press menu button
192 MSInstanceMessageHook1(void, SpringBoard, handleKeyEvent, GSEventRef, event) {
193 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
194 return MSOldCall(event);
196 return MSSuperCall(event);
201 MSInstanceMessageHook0(void, SBLockScreenViewController, _toggleMediaControls) {
206 MSInstanceMessageHook0(BOOL, SBLockScreenViewController, handleMenuButtonDoubleTap) {
208 BOOL value(MSOldCall());
209 if (kCFCoreFoundationVersionNumber >= 800)
210 [self _toggleMediaControls];
214 MSInstanceMessageHook0(BOOL, SBLockScreenManager, handleMenuButtonTap) {
216 BOOL value(MSOldCall());
219 if (!value && menu_) {
220 if (active_ != _not(size_t))
221 [[cydgets_ objectAtIndex:active_] disableCydget];
222 active_ = (active_ + 1) % [cydgets_ count];
223 [[cydgets_ objectAtIndex:active_] enableCydget];
224 // XXX: or siri doesn't disappear correctly
225 return kCFCoreFoundationVersionNumber >= 800 ? value : YES;
234 for (active_ = 0; active_ != [cydgets_ count]; ++active_)
235 if ([[[cydgets_ objectAtIndex:active_] objectForKey:@"CYName"] isEqualToString:@"AwayView"])
237 if (active_ == [cydgets_ count])
238 active_ = _not(size_t);
240 if (active_ != _not(size_t))
241 [[cydgets_ objectAtIndex:active_] enableCydget];
244 static void Undim_(SBAwayController *self) {
245 if ([self isDimmed]) {
247 [[[self awayView] window] makeKeyWindow];
251 MSInstanceMessageHook1(void, SBLockScreenViewController, startLockScreenFadeInAnimationForSource, int, source) {
256 MSInstanceMessageHook0(void, SBAwayController, undimScreen) {
261 MSInstanceMessageHook1(void, SBAwayController, undimScreen, BOOL, undim) {
266 static void Deactivate_() {
267 if (active_ != _not(size_t))
268 [[cydgets_ objectAtIndex:active_] disableCydget];
273 MSHook(void, BKSDisplayServicesSetScreenBlanked, BOOL blanked) {
276 _BKSDisplayServicesSetScreenBlanked(blanked);
280 MSHookFunction("_BKSDisplayServicesSetScreenBlanked", MSHake(BKSDisplayServicesSetScreenBlanked));
283 MSInstanceMessageHook1(void, SBUserAgent, dimScreen, BOOL, dim) {
288 MSInstanceMessageHook0(void, SpringBoard, _menuButtonWasHeld) {
293 MSInstanceMessageHook1(void, SpringBoard, _menuButtonDown, GSEventRef, event) {
298 MSInstanceMessageHook1(void, SpringBoard, menuButtonDown, GSEventRef, event) {
303 MSInstanceMessageHook1(void, SBAwayView, addGestureRecognizer, id, recognizer) {
304 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
305 return MSOldCall(recognizer);
308 MSInstanceMessageHook1(void, SBAwayWindow, sendGSEvent, GSEventRef, event) {
309 if (online_ == 0 || kCFCoreFoundationVersionNumber > 600)
310 return MSOldCall(event);
312 return MSSuperCall(event);
315 MSInstanceMessageHook2(void, SBLockScreenView, setMediaControlsHidden,forRequester, BOOL, hidden, id, requester) {
316 MSOldCall(hidden, requester);
317 UIView *&_mediaControlsContainerView(MSHookIvar<UIView *>(self, "_mediaControlsContainerView"));
318 UIView *&_mediaControlsView(MSHookIvar<UIView *>(self, "_mediaControlsView"));
319 if (&_mediaControlsContainerView != NULL && &_mediaControlsView != NULL)
320 [_mediaControlsContainerView setUserInteractionEnabled:([_mediaControlsView alpha] != 0)];
323 #define Cydgets_ @"/System/Library/LockCydgets"
325 static void CydgetSetup() {
326 NSString *plist([NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.Cydget.plist", NSHomeDirectory()]);
327 settings_ = [NSMutableDictionary dictionaryWithContentsOfFile:plist] ?: [NSMutableDictionary dictionary];
329 nowplaying_ = [[settings_ objectForKey:@"NowPlaying"] boolValue];
330 remember_ = [[settings_ objectForKey:@"RememberPosition"] boolValue];
332 NSArray *cydgets([settings_ objectForKey:@"LockCydgets"] ?: [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:
333 @"Welcome", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
334 ], [NSDictionary dictionaryWithObjectsAndKeys:
335 @"AwayView", @"Name", [NSNumber numberWithBool:YES], @"Active", nil
338 cydgets_ = [NSMutableArray arrayWithCapacity:4];
339 for (NSDictionary *cydget in cydgets)
340 if ([[cydget objectForKey:@"Active"] boolValue]) {
341 NSString *name([cydget objectForKey:@"Name"]);
342 NSString *path([NSString stringWithFormat:@"%@/%@.cydget/Info.plist", Cydgets_, name]);
343 if (NSMutableDictionary *info = [NSMutableDictionary dictionaryWithContentsOfFile:path]) {
344 [info setObject:name forKey:@"CYName"];
345 [info setObject:path forKey:@"CYPath"];
346 [cydgets_ addObject:info];
350 if ([cydgets_ count] == 0)
354 // XXX: this could happen while it is unlocked
355 MSInstanceMessageHook1(id, SBLockScreenView, initWithFrame, CGRect, frame) {
356 self = MSOldCall(frame);
362 MSInstanceMessageHook1(void, SBAwayController, _finishedUnlockAttemptWithStatus, BOOL, status) {
368 // this is called occasionally by -[SBAwayView updateInterface] and takes away our keyboard
369 MSInstanceMessageHook0(void, SBAwayView, _fixupFirstResponder) {
374 MSInstanceMessageHook0(void, SBAwayView, updateInterface) {
377 NSDictionary *cydget([cydgets_ objectAtIndex:active_]);
379 NSString *background([cydget objectForKey:@"CYBackground"]);
380 if ([background isEqualToString:@"Wallpaper"]) {
381 MSIvarHook(UIView *, _backgroundView);
382 [_backgroundView setAlpha:1.0f];
385 if ([[cydget objectForKey:@"CYShowDateTime"] boolValue])