├── .gitignore ├── .vscode └── settings.json ├── HomeDockX.plist ├── LICENSE ├── Makefile ├── README.md ├── control ├── prefsbundle ├── HomeDockXRootListController.h ├── HomeDockXRootListController.m ├── Makefile ├── Resources │ ├── HomeDockX.png │ ├── HomeDockX@2x.png │ ├── Info.plist │ └── Root.plist └── entry.plist ├── screenshots ├── screenshot_1.png └── screenshot_2.png └── src ├── Dock.xm ├── HomeDockXPreference.m ├── HomeGesture.xm ├── KeyboardStateListener.m ├── Medusa.xm └── includes ├── HomeDockXPreference.h ├── KeyboardStateListener.h └── common.h /.gitignore: -------------------------------------------------------------------------------- 1 | .theos/ 2 | obj/ 3 | packages/ 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.h": "objective-c", 4 | "*.xm": "objective-c", 5 | "*.plist": "xml" 6 | } 7 | } -------------------------------------------------------------------------------- /HomeDockX.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.springboard" ); }; } 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Brian Choi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export ARCHS = arm64 2 | 3 | include $(THEOS)/makefiles/common.mk 4 | 5 | TWEAK_NAME = HomeDockX 6 | HomeDockX_FILES = src/Dock.xm src/HomeGesture.xm src/Medusa.xm src/KeyboardStateListener.m src/HomeDockXPreference.m 7 | 8 | include $(THEOS_MAKE_PATH)/tweak.mk 9 | 10 | after-install:: 11 | install.exec "killall -9 SpringBoard" 12 | 13 | SUBPROJECTS += prefsbundle 14 | include $(THEOS_MAKE_PATH)/aggregate.mk 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 |
4 | HomeDockX 5 |

6 |

7 | iPhone X gesture with perfectly compatible iPad floating dock
8 | Works well on iOS 11.3.1 with Electra1131
9 |
10 | This tweak is designed for classic iPhones (iPhone that without notch) 11 |

12 | 13 | 14 | ## Features 15 | - Enable iPhone X home gesture 16 | - Enable iPad floating dock 17 | - Enable in-app floating dock 18 | - Dismiss in-app dock by pressing physical home button 19 | - Enable iPad Slide Over 20 | - Fix landscape only app being launched in portrait because of Slide Over 21 | - iPad Slide Over app blacklisting 22 | - Disable home gesture when using keyboard 23 | - Bridge home gesture and floating dock gesture * 24 | 25 | 26 | ``` 27 | * If you enable both home gesture and in-app dock together, 28 | swipe up from left of the screen will activate in-app dock. 29 | Right for home gesture. 30 | ``` 31 | 32 | 33 | ## Screenshots 34 | ![Screenshot](screenshots/screenshot_1.png) 35 | ![Screenshot](screenshots/screenshot_2.png) 36 | 37 | 38 | ## Downloads 39 | See [Release](https://github.com/brian9206/HomeDockX/releases/latest) 40 | 41 | 42 | ## Credits 43 | This tweak used some code from the following tweaks with adjustments. 44 | - [Home Gesture](https://github.com/VitaTaf/HomeGesture) by VitaTaf 45 | - [FloatingDockXI](https://github.com/KpwnZ/FloatingDockXI) by KpwnZ 46 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: pw.ssnull.homedockx 2 | Name: HomeDockX 3 | Depends: firmware (>= 11.0), mobilesubstrate, preferenceloader, applist 4 | Version: 1.2.3 5 | Architecture: iphoneos-arm 6 | Description: iPhone X gesture with perfectly compatible iPad floating dock 7 | Maintainer: Brian Choi 8 | Author: Brian Choi 9 | Section: Tweaks 10 | -------------------------------------------------------------------------------- /prefsbundle/HomeDockXRootListController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface HomeDockXRootListController : PSListController 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /prefsbundle/HomeDockXRootListController.m: -------------------------------------------------------------------------------- 1 | #include "HomeDockXRootListController.h" 2 | 3 | @implementation HomeDockXRootListController 4 | 5 | - (NSArray *)specifiers { 6 | if (!_specifiers) { 7 | _specifiers = [[self loadSpecifiersFromPlistName:@"Root" target:self] retain]; 8 | } 9 | 10 | return _specifiers; 11 | } 12 | 13 | - (void)respring { 14 | CFPreferencesSynchronize(CFSTR("pw.ssnull.homedockx"), kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 15 | 16 | #pragma GCC diagnostic push 17 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 18 | system("killall SpringBoard"); 19 | #pragma GCC diagnostic pop 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /prefsbundle/Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | 3 | BUNDLE_NAME = HomeDockXPrefs 4 | HomeDockXPrefs_FILES = HomeDockXRootListController.m 5 | HomeDockXPrefs_INSTALL_PATH = /Library/PreferenceBundles 6 | HomeDockXPrefs_FRAMEWORKS = UIKit 7 | HomeDockXPrefs_PRIVATE_FRAMEWORKS = Preferences 8 | BetterMedusaPreference_LIBRARIES = applist 9 | 10 | include $(THEOS_MAKE_PATH)/bundle.mk 11 | 12 | internal-stage:: 13 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END) 14 | $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/HomeDockXPrefs.plist$(ECHO_END) 15 | -------------------------------------------------------------------------------- /prefsbundle/Resources/HomeDockX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brian9206/HomeDockX/a22752377e18586941be57dbbb496a9d2d9fac07/prefsbundle/Resources/HomeDockX.png -------------------------------------------------------------------------------- /prefsbundle/Resources/HomeDockX@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brian9206/HomeDockX/a22752377e18586941be57dbbb496a9d2d9fac07/prefsbundle/Resources/HomeDockX@2x.png -------------------------------------------------------------------------------- /prefsbundle/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | HomeDockXPrefs 9 | CFBundleIdentifier 10 | pw.ssnull.homedockx 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0 21 | NSPrincipalClass 22 | HomeDockXRootListController 23 | 24 | 25 | -------------------------------------------------------------------------------- /prefsbundle/Resources/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | title 6 | HomeDockX 7 | 8 | items 9 | 10 | 11 | cell 12 | PSGroupCell 13 | 14 | label 15 | Tweaks 16 | 17 | footerText 18 | You need to enable floating dock and in-app dock in order to use Slide Over. 19 | 20 | 21 | 22 | cell 23 | PSSwitchCell 24 | 25 | default 26 | 27 | 28 | defaults 29 | pw.ssnull.homedockx 30 | 31 | key 32 | enableHomeGesture 33 | 34 | label 35 | Enable Home Gesture 36 | 37 | 38 | 39 | cell 40 | PSSwitchCell 41 | 42 | default 43 | 44 | 45 | defaults 46 | pw.ssnull.homedockx 47 | 48 | key 49 | enableDock 50 | 51 | label 52 | Enable Floating Dock 53 | 54 | 55 | 56 | cell 57 | PSSwitchCell 58 | 59 | default 60 | 61 | 62 | defaults 63 | pw.ssnull.homedockx 64 | 65 | key 66 | enableMedusa 67 | 68 | label 69 | Enable Slide Over 70 | 71 | 72 | 73 | cell 74 | PSGroupCell 75 | 76 | label 77 | Home Gesture 78 | 79 | footerText 80 | Home gesture will automatically be disabled when you are using keyboard to prevent triggering home gesture by mistake when typing. 81 | 82 | 83 | 84 | cell 85 | PSSwitchCell 86 | 87 | default 88 | 89 | 90 | defaults 91 | pw.ssnull.homedockx 92 | 93 | key 94 | noKeyboard 95 | 96 | label 97 | Disable When Using Keyboard 98 | 99 | 100 | 101 | cell 102 | PSGroupCell 103 | 104 | label 105 | In-App Dock 106 | 107 | footerText 108 | If you enable In-App dock, you can swipe up from left of the screen to activate dock in-app. Right for home gesture. 109 | 110 | 111 | 112 | cell 113 | PSSwitchCell 114 | 115 | default 116 | 117 | 118 | defaults 119 | pw.ssnull.homedockx 120 | 121 | key 122 | enableInAppDock 123 | 124 | label 125 | Enable In-App Dock 126 | 127 | 128 | 129 | cell 130 | PSSwitchCell 131 | 132 | default 133 | 134 | 135 | defaults 136 | pw.ssnull.homedockx 137 | 138 | key 139 | enableHomeButtonDismiss 140 | 141 | label 142 | Dismiss by Home Button 143 | 144 | 145 | 146 | cell 147 | PSGroupCell 148 | 149 | label 150 | Dock Suggestion 151 | 152 | 153 | 154 | cell 155 | PSSwitchCell 156 | 157 | default 158 | 159 | 160 | defaults 161 | pw.ssnull.homedockx 162 | 163 | key 164 | enableDockSuggestion 165 | 166 | label 167 | Enable App Suggestion 168 | 169 | 170 | 171 | cell 172 | PSSwitchCell 173 | 174 | default 175 | 176 | 177 | defaults 178 | pw.ssnull.homedockx 179 | 180 | key 181 | showRecentApp 182 | 183 | label 184 | Show Recent App 185 | 186 | 187 | 188 | cell 189 | PSGroupCell 190 | 191 | label 192 | Maximum Number of Suggestion App 193 | 194 | 195 | 196 | cell 197 | PSSliderCell 198 | 199 | default 200 | 2 201 | 202 | min 203 | 1 204 | 205 | max 206 | 5 207 | 208 | showValue 209 | 210 | 211 | defaults 212 | pw.ssnull.homedockx 213 | 214 | key 215 | maxSuggestion 216 | 217 | label 218 | Enable App Suggestion 219 | 220 | isSegmented 221 | 222 | 223 | segmentCount 224 | 0.1 225 | 226 | 227 | 228 | cell 229 | PSGroupCell 230 | 231 | label 232 | Slide Over 233 | 234 | 235 | 236 | bundle 237 | AppList 238 | 239 | cell 240 | PSLinkCell 241 | 242 | isController 243 | 244 | 245 | label 246 | Blacklisted Apps 247 | 248 | ALSettingsPath 249 | /var/mobile/Library/Preferences/pw.ssnull.homedockx.plist 250 | 251 | ALSettingsKeyPrefix 252 | blacklisted- 253 | 254 | ALChangeNotification 255 | pw.ssnull.homedockx/settingschanged 256 | 257 | ALAllowsSelection 258 | 1 259 | 260 | ALSectionDescriptors 261 | 262 | 263 | title 264 | User Applications 265 | 266 | predicate 267 | isSystemApplication = FALSE 268 | 269 | cell-class-name 270 | ALSwitchCell 271 | 272 | icon-size 273 | 29 274 | 275 | suppress-hidden-apps 276 | 1 277 | 278 | 279 | 280 | title 281 | System Applications 282 | 283 | predicate 284 | isSystemApplication = TRUE 285 | 286 | cell-class-name 287 | ALSwitchCell 288 | 289 | icon-size 290 | 29 291 | 292 | suppress-hidden-apps 293 | 1 294 | 295 | 296 | 297 | 298 | 299 | cell 300 | PSGroupCell 301 | 302 | 303 | 304 | cell 305 | PSButtonCell 306 | 307 | label 308 | Apply Settings (Respring) 309 | 310 | action 311 | respring 312 | 313 | 314 | 315 | cell 316 | PSGroupCell 317 | 318 | footerText 319 | Created by Brian Choi 320 | 321 | 322 | 323 | cell 324 | PSStaticTextCell 325 | 326 | label 327 | Credits: VitaTaf, KpwnZ 328 | 329 | 330 | 331 | -------------------------------------------------------------------------------- /prefsbundle/entry.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entry 6 | 7 | bundle 8 | HomeDockXPrefs 9 | cell 10 | PSLinkCell 11 | detail 12 | HomeDockXRootListController 13 | icon 14 | HomeDockX.png 15 | isController 16 | 17 | label 18 | HomeDockX 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /screenshots/screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brian9206/HomeDockX/a22752377e18586941be57dbbb496a9d2d9fac07/screenshots/screenshot_1.png -------------------------------------------------------------------------------- /screenshots/screenshot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brian9206/HomeDockX/a22752377e18586941be57dbbb496a9d2d9fac07/screenshots/screenshot_2.png -------------------------------------------------------------------------------- /src/Dock.xm: -------------------------------------------------------------------------------- 1 | /** 2 | * iPad Floating Dock implementation 3 | * Credit: KpwnZ for his FloatingDockXI 4 | */ 5 | #include "includes/common.h" 6 | 7 | @interface SBGrabberTongue : NSObject 8 | -(UIGestureRecognizer *)edgePullGestureRecognizer; 9 | @end 10 | 11 | @interface SBFloatingDockRootViewController : UIViewController 12 | @property (nonatomic,retain) SBGrabberTongue *grabberTongue; 13 | @end 14 | 15 | @interface SBFloatingDockController : NSObject 16 | @property (nonatomic, retain) SBFloatingDockRootViewController *viewController; 17 | -(void)_dismissFloatingDockIfPresentedAnimated:(BOOL)arg1 completionHandler:(/*^block*/id)arg2; 18 | -(BOOL)isFloatingDockPresented; 19 | @end 20 | 21 | %group Dock 22 | 23 | // global variable 24 | SBFloatingDockController *dock = NULL; 25 | 26 | // Enable floating dock 27 | %hook SBFloatingDockController 28 | - (id)initWithIconController:(id)arg1 applicationController:(id)arg2 recentsController:(id)arg3 recentsDataStore:(id)arg4 transitionCoordinator:(id)arg5 appSuggestionManager:(id)arg6 analyticsClient:(id)arg7 { 29 | dock = self; 30 | return %orig; 31 | } 32 | 33 | + (BOOL)isFloatingDockSupported { 34 | return YES; 35 | } 36 | 37 | - (BOOL)_systemGestureManagerAllowsFloatingDockGesture { 38 | if (prefs.enableHomeGesture) { 39 | // get all the information that we need 40 | UIGestureRecognizer *edgePullGestureRecognizer = [[self.viewController grabberTongue] edgePullGestureRecognizer]; 41 | CGPoint location = [edgePullGestureRecognizer locationInView:edgePullGestureRecognizer.view]; 42 | 43 | bool stop = NO; 44 | 45 | switch ([(SpringBoard*)[UIApplication sharedApplication] activeInterfaceOrientation]) { 46 | case 1: // UIInterfaceOrientationPortrait 47 | stop = location.x > edgePullGestureRecognizer.view.bounds.size.width / 2; 48 | break; 49 | 50 | case 2: // UIInterfaceOrientationPortraitUpsideDown 51 | stop = location.x < edgePullGestureRecognizer.view.bounds.size.width / 2; 52 | break; 53 | } 54 | 55 | if (stop) { 56 | return NO; 57 | } 58 | } 59 | 60 | return prefs.enableInAppDock; 61 | } 62 | %end 63 | 64 | // Enable floating dock recent app / suggestion 65 | %hook SBFloatingDockSuggestionsModel 66 | - (id)initWithMaximumNumberOfSuggestions:(unsigned long long)maxSuggestion iconController:(id)arg2 recentsController:(id)arg3 recentsDataStore:(id)arg4 recentsDefaults:(id)arg5 floatingDockDefaults:(id)arg6 appSuggestionManager:(id)arg7 analyticsClient:(id)arg8 { 67 | maxSuggestion = prefs.maxSuggestion; 68 | return %orig; 69 | } 70 | 71 | - (BOOL)_shouldProcessAppSuggestion:(id)arg1 { 72 | return prefs.enableDockSuggestion; 73 | } 74 | 75 | - (BOOL)recentsEnabled { 76 | return prefs.showRecentApp; 77 | } 78 | %end 79 | 80 | // Dismiss floating dock by pressing home button 81 | %hook SBHomeHardwareButton 82 | - (void)singlePressUp:(id)arg1 { 83 | if (prefs.enableHomeButtonDismiss) { 84 | // must not be springboard and dock must be swiped up 85 | if (!isSpringBoardAtFront && dock && [dock isFloatingDockPresented]) { 86 | [dock _dismissFloatingDockIfPresentedAnimated:YES completionHandler:NULL]; 87 | return; 88 | } 89 | } 90 | 91 | %orig; 92 | } 93 | %end 94 | 95 | // Disable home screen rotation because of layout bug 96 | %hook SpringBoard 97 | - (BOOL)homeScreenSupportsRotation { 98 | return NO; 99 | } 100 | 101 | - (BOOL)homeScreenRotationStyle { 102 | return 0; 103 | } 104 | %end 105 | 106 | %end // %group Dock 107 | 108 | %ctor { 109 | if (prefs.enableDock) { 110 | %init(Dock); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/HomeDockXPreference.m: -------------------------------------------------------------------------------- 1 | /** 2 | * HomeDockXPreference 3 | */ 4 | #import "includes/HomeDockXPreference.h" 5 | 6 | static HomeDockXPreference *sharedInstance = NULL; 7 | 8 | @implementation HomeDockXPreference { 9 | NSDictionary *_prefsDict; 10 | } 11 | 12 | + (HomeDockXPreference *)sharedInstance { 13 | if (!sharedInstance) { 14 | sharedInstance = [[HomeDockXPreference alloc] init]; 15 | } 16 | 17 | return sharedInstance; 18 | } 19 | 20 | - (id)init { 21 | if ((self = [super init])) { 22 | NSDictionary *prefs = NULL; 23 | 24 | CFStringRef appID = CFSTR("pw.ssnull.homedockx"); 25 | CFArrayRef keyList = CFPreferencesCopyKeyList(appID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 26 | 27 | if (keyList) { 28 | prefs = (NSDictionary *)CFPreferencesCopyMultiple(keyList, appID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 29 | CFRelease(keyList); 30 | } 31 | 32 | if (prefs) { 33 | self.enableHomeGesture = [[prefs objectForKey:@"enableHomeGesture"] boolValue]; 34 | self.enableDock = [[prefs objectForKey:@"enableDock"] boolValue]; 35 | self.enableMedusa = [[prefs objectForKey:@"enableMedusa"] boolValue]; 36 | 37 | self.noKeyboard = [[prefs objectForKey:@"noKeyboard"] boolValue]; 38 | 39 | self.enableInAppDock = [[prefs objectForKey:@"enableInAppDock"] boolValue]; 40 | self.enableHomeButtonDismiss = [[prefs objectForKey:@"enableHomeButtonDismiss"] boolValue]; 41 | self.enableDockSuggestion = [[prefs objectForKey:@"enableDockSuggestion"] boolValue]; 42 | self.showRecentApp = [[prefs objectForKey:@"showRecentApp"] boolValue]; 43 | self.maxSuggestion = [[prefs objectForKey:@"maxSuggestion"] intValue]; 44 | 45 | if (self.maxSuggestion <= 0) { 46 | self.maxSuggestion = 2; 47 | } 48 | } 49 | 50 | _prefsDict = prefs; 51 | } 52 | 53 | return self; 54 | } 55 | 56 | - (BOOL)isBlacklisted:(NSString*)bundleIdentifier { 57 | if (!_prefsDict) { 58 | return NO; 59 | } 60 | 61 | NSString *value = [_prefsDict objectForKey:[@"blacklisted-" stringByAppendingString:bundleIdentifier]]; 62 | 63 | if (!value) { 64 | return NO; 65 | } 66 | 67 | return YES; 68 | } 69 | 70 | @end 71 | 72 | -------------------------------------------------------------------------------- /src/HomeGesture.xm: -------------------------------------------------------------------------------- 1 | /** 2 | * Home Gesture implementation 3 | * Credit: VitaTaf for his Home Gesture 4 | */ 5 | #include "includes/common.h" 6 | 7 | @interface SBHomeGesturePanGestureRecognizer 8 | -(void)reset; 9 | -(void)touchesEnded:(id)arg1 withEvent:(id)arg2; 10 | @end 11 | 12 | %group HomeGesture 13 | 14 | // global variable 15 | long _dismissalSlidingMode = 0; 16 | BOOL _isDashboardActive = 0; 17 | int applicationDidFinishLaunching; 18 | bool originalButton; 19 | long _homeButtonType = 1; 20 | 21 | // Enable home gestures 22 | %hook BSPlatform 23 | - (NSInteger)homeButtonType { 24 | _homeButtonType = %orig; 25 | 26 | if (originalButton) { 27 | originalButton = NO; 28 | return %orig; 29 | } 30 | else { 31 | return 2; 32 | } 33 | } 34 | %end 35 | 36 | // Hide home bar 37 | %hook MTLumaDodgePillView 38 | - (id)initWithFrame:(struct CGRect)arg1 { 39 | return NULL; 40 | } 41 | %end 42 | 43 | // Workaround for status bar transition bug 44 | %hook CCUIOverlayStatusBarPresentationProvider 45 | - (void)_addHeaderContentTransformAnimationToBatch:(id)arg1 transitionState:(id)arg2 { 46 | return; 47 | } 48 | 49 | - (struct CGRect)headerViewFrame { 50 | struct CGRect orig = %orig; 51 | orig.size.height = 45; 52 | return (CGRect){orig.origin, orig.size}; 53 | } 54 | 55 | - (struct CGRect)_presentedViewFrame { 56 | struct CGRect orig = %orig; 57 | orig.origin.y = 45; 58 | return (CGRect){orig.origin, orig.size}; 59 | } 60 | %end 61 | 62 | // Prevent status bar from flashing when invoking control center 63 | %hook CCUIModularControlCenterOverlayViewController 64 | - (void)setOverlayStatusBarHidden:(bool)arg1 { 65 | return; 66 | } 67 | %end 68 | 69 | // Prevent status bar from displaying in fullscreen when invoking control center 70 | %hook CCUIStatusBarStyleSnapshot 71 | - (bool)isHidden { 72 | return YES; 73 | } 74 | %end 75 | 76 | // Hide home bar in cover sheet 77 | %hook SBDashboardHomeAffordanceView 78 | - (void)_createStaticHomeAffordance { 79 | return; 80 | } 81 | %end 82 | 83 | // Workaround for TouchID respring bug 84 | %hook SBCoverSheetSlidingViewController 85 | - (void)_finishTransitionToPresented:(_Bool)arg1 animated:(_Bool)arg2 withCompletion:(id)arg3 { 86 | if ((_dismissalSlidingMode != 1) && (arg1 == 0)) { 87 | return; 88 | } else { 89 | %orig; 90 | } 91 | } 92 | - (long long)dismissalSlidingMode { 93 | _dismissalSlidingMode = %orig; 94 | return %orig; 95 | } 96 | %end 97 | 98 | %hook SBHomeGesturePanGestureRecognizer 99 | void resetTouch(SBHomeGesturePanGestureRecognizer *self, NSSet *touches, id event) { 100 | // stop touching the screen 101 | [self touchesEnded: touches withEvent: event]; 102 | [self reset]; 103 | } 104 | 105 | - (void)touchesBegan:(NSSet *)touches withEvent:(id)event { 106 | if (!_isDashboardActive) { 107 | UITouch *touch = [touches anyObject]; 108 | 109 | // do not do home gesture for 1/2 left of the screen if dock is enabled 110 | if (!isSpringBoardAtFront && 111 | prefs.enableDock 112 | ) { 113 | BOOL reset = NO; 114 | 115 | switch ([(SpringBoard*)[UIApplication sharedApplication] activeInterfaceOrientation]) { 116 | case 1: // UIInterfaceOrientationPortrait 117 | reset = [touch locationInView:touch.view].x < touch.view.bounds.size.width / 2; 118 | break; 119 | 120 | case 2: // UIInterfaceOrientationPortraitUpsideDown 121 | reset = [touch locationInView:touch.view].x > touch.view.bounds.size.width / 2; 122 | break; 123 | 124 | case 3: // UIInterfaceOrientationLandscapeRight 125 | case 4: // UIInterfaceOrientationLandscapeLeft 126 | reset = YES; 127 | } 128 | 129 | if (reset) { 130 | resetTouch(self, touches, event); 131 | return; 132 | } 133 | } 134 | 135 | // do not enable home gesture when using keyboard 136 | if (prefs.noKeyboard && isKeyboardVisible) { 137 | resetTouch(self, touches, event); 138 | return; 139 | } 140 | } 141 | 142 | return %orig; 143 | } 144 | %end 145 | 146 | // Listen for dashboard active state change 147 | %hook SBDashBoardViewController 148 | - (void)viewDidLoad { 149 | originalButton = YES; 150 | %orig; 151 | } 152 | 153 | - (void)viewWillAppear:(_Bool)arg1 { 154 | %orig; 155 | _isDashboardActive = YES; 156 | } 157 | 158 | - (void)viewWillDisappear:(_Bool)arg1 { 159 | %orig; 160 | _isDashboardActive = NO; 161 | } 162 | %end 163 | 164 | // Workaround for crash when launching app and invoking control center simultaneously 165 | %hook SBSceneHandle 166 | - (id)scene { 167 | @try { 168 | return %orig; 169 | } 170 | @catch (NSException *e) { 171 | return nil; 172 | } 173 | } 174 | %end 175 | 176 | // Restore screenshot shortcut 177 | %hook SpringBoard 178 | - (void)applicationDidFinishLaunching:(id)arg1 { 179 | applicationDidFinishLaunching = 2; 180 | %orig; 181 | } 182 | %end 183 | 184 | %hook SBPressGestureRecognizer 185 | - (void)setAllowedPressTypes:(NSArray *)arg1 { 186 | NSArray * lockHome = @[@104, @101]; 187 | NSArray * lockVol = @[@104, @102, @103]; 188 | if ([arg1 isEqual:lockVol] && applicationDidFinishLaunching == 2) { 189 | %orig(lockHome); 190 | applicationDidFinishLaunching--; 191 | return; 192 | } 193 | %orig; 194 | } 195 | %end 196 | 197 | %hook SBClickGestureRecognizer 198 | - (void)addShortcutWithPressTypes:(id)arg1 { 199 | if (applicationDidFinishLaunching == 1) { 200 | applicationDidFinishLaunching--; 201 | return; 202 | } 203 | %orig; 204 | } 205 | %end 206 | 207 | %hook SBHomeHardwareButton 208 | - (id)initWitHomeButtonType:(long long)arg1 { 209 | return %orig(_homeButtonType); 210 | } 211 | 212 | - (id)initWithScreenshotGestureRecognizer:(id)arg1 homeButtonType:(long long)arg2 buttonActions:(id)arg3 gestureRecognizerConfiguration:(id)arg4 { 213 | return %orig(arg1, _homeButtonType, arg3, arg4); 214 | } 215 | - (id)initWithScreenshotGestureRecognizer:(id)arg1 homeButtonType:(long long)arg2 { 216 | return %orig(arg1, _homeButtonType); 217 | } 218 | %end 219 | 220 | // Restore button to invoke Siri 221 | %hook SBLockHardwareButtonActions 222 | - (id)initWithHomeButtonType:(long long)arg1 proximitySensorManager:(id)arg2 { 223 | return %orig(_homeButtonType, arg2); 224 | } 225 | %end 226 | 227 | // Force close app without long-pressing card 228 | %hook SBAppSwitcherSettings 229 | - (long long)effectiveKillAffordanceStyle { 230 | return 2; 231 | } 232 | %end 233 | 234 | // Enable simutaneous scrolling and dismissing 235 | %hook SBFluidSwitcherViewController 236 | - (double)_killGestureHysteresis { 237 | double orig = %orig; 238 | return orig == 30 ? 10 : orig; 239 | } 240 | 241 | %end 242 | 243 | %end // end %group HomeGesture 244 | 245 | %ctor { 246 | if (prefs.enableHomeGesture) { 247 | %init(HomeGesture); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/KeyboardStateListener.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copied and modified from: https://stackoverflow.com/questions/1490573/how-can-i-programmatically-check-whether-a-keyboard-is-present-in-ios-app 3 | * Credits: rpetrich, drawnonward 4 | */ 5 | #import "includes/KeyboardStateListener.h" 6 | 7 | static KeyboardStateListener *sharedInstance; 8 | 9 | @implementation KeyboardStateListener 10 | 11 | + (KeyboardStateListener *)sharedInstance { 12 | return sharedInstance; 13 | } 14 | 15 | + (void)load { 16 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 17 | sharedInstance = [[self alloc] init]; 18 | [pool release]; 19 | } 20 | 21 | - (BOOL)isVisible { 22 | return _isVisible; 23 | } 24 | 25 | - (void)didShow { 26 | _isVisible = YES; 27 | } 28 | 29 | - (void)didHide { 30 | _isVisible = NO; 31 | } 32 | 33 | - (id)init { 34 | if ((self = [super init])) { 35 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 36 | [center addObserver:self selector:@selector(didShow) name:UIKeyboardDidShowNotification object:nil]; 37 | [center addObserver:self selector:@selector(didHide) name:UIKeyboardWillHideNotification object:nil]; 38 | } 39 | 40 | return self; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /src/Medusa.xm: -------------------------------------------------------------------------------- 1 | /** 2 | * Medusa 3 | */ 4 | #import "includes/common.h" 5 | 6 | %group Medusa 7 | 8 | // global variable 9 | NSMutableDictionary *capableApps = [NSMutableDictionary new]; 10 | 11 | // Enable medusa 12 | %hook SBPlatformController 13 | - (long long)medusaCapabilities { 14 | return 1; 15 | } 16 | %end 17 | 18 | %hook SBMainWorkspace 19 | - (BOOL)isMedusaEnabled { 20 | return YES; 21 | } 22 | %end 23 | 24 | %hook SBApplication 25 | - (BOOL)isMedusaCapable { 26 | if (!self.bundleIdentifier || [self isClassic]) { 27 | return NO; 28 | } 29 | 30 | // check the cached list first 31 | NSString *capable = [capableApps objectForKey:self.bundleIdentifier]; 32 | 33 | if (capable) { 34 | return [capable intValue] == 1; 35 | } 36 | 37 | // check blacklisted app 38 | if ([prefs isBlacklisted:self.bundleIdentifier]) { 39 | return %orig; 40 | } 41 | 42 | // bundle ID is not exists in the cache. 43 | NSMutableDictionary *infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:[[self.info.executableURL.path stringByDeletingLastPathComponent] stringByAppendingString:@"/Info.plist"]]; 44 | 45 | if (infoDict == NULL) { 46 | return %orig; 47 | } 48 | 49 | // disable landscape only app to prevent orientation bug 50 | NSArray *orientations = [infoDict valueForKeyPath:@"UISupportedInterfaceOrientations"]; 51 | 52 | if (orientations != NULL && [orientations indexOfObject:@"UIInterfaceOrientationPortrait"] == NSNotFound) { 53 | [capableApps setObject:[NSNumber numberWithInt:0] forKey:self.bundleIdentifier]; 54 | return NO; 55 | } 56 | 57 | [capableApps setObject:[NSNumber numberWithInt:1] forKey:self.bundleIdentifier]; 58 | return YES; 59 | } 60 | %end 61 | 62 | %end // end %group Medusa 63 | 64 | %ctor { 65 | if (prefs.enableMedusa) { 66 | %init(Medusa); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/includes/HomeDockXPreference.h: -------------------------------------------------------------------------------- 1 | /** 2 | * HomeDockXPreference 3 | */ 4 | @interface HomeDockXPreference : NSObject 5 | 6 | @property(nonatomic) BOOL enableHomeGesture; 7 | @property(nonatomic) BOOL enableDock; 8 | @property(nonatomic) BOOL enableMedusa; 9 | 10 | // home gesture 11 | @property(nonatomic) BOOL noKeyboard; 12 | 13 | // dock 14 | @property(nonatomic) BOOL enableInAppDock; 15 | @property(nonatomic) BOOL enableHomeButtonDismiss; 16 | @property(nonatomic) BOOL enableDockSuggestion; 17 | @property(nonatomic) BOOL showRecentApp; 18 | @property(nonatomic) int maxSuggestion; 19 | 20 | // constructor 21 | - (id)init; 22 | - (BOOL)isBlacklisted:(NSString*)bundleIdentifier; 23 | 24 | + (HomeDockXPreference *)sharedInstance; 25 | 26 | @end -------------------------------------------------------------------------------- /src/includes/KeyboardStateListener.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copied and modified from: https://stackoverflow.com/questions/1490573/how-can-i-programmatically-check-whether-a-keyboard-is-present-in-ios-app 3 | * Credits: rpetrich, drawnonward 4 | */ 5 | @interface KeyboardStateListener : NSObject { 6 | BOOL _isVisible; 7 | } 8 | 9 | + (KeyboardStateListener *)sharedInstance; 10 | @property (nonatomic, readonly, getter=isVisible) BOOL visible; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /src/includes/common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Common header 3 | */ 4 | #import "includes/HomeDockXPreference.h" 5 | #import "includes/KeyboardStateListener.h" 6 | 7 | @interface FBApplicationInfo 8 | @property (nonatomic,retain,readonly) NSURL *executableURL; 9 | @end 10 | 11 | @interface SBApplicationInfo : FBApplicationInfo 12 | @end 13 | 14 | @interface SBApplication 15 | @property (nonatomic,readonly) NSString *bundleIdentifier; 16 | @property (nonatomic,readonly) SBApplicationInfo *info; 17 | 18 | -(BOOL)isClassic; 19 | @end 20 | 21 | @interface SpringBoard : UIApplication 22 | - (SBApplication*)_accessibilityFrontMostApplication; 23 | - (long long)activeInterfaceOrientation; 24 | @end 25 | 26 | #define isSpringBoardAtFront (![(SpringBoard*)[UIApplication sharedApplication] _accessibilityFrontMostApplication].bundleIdentifier) 27 | #define isKeyboardVisible ([KeyboardStateListener sharedInstance].visible) 28 | #define prefs [HomeDockXPreference sharedInstance] 29 | --------------------------------------------------------------------------------