├── .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 | 
35 | 
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 |
--------------------------------------------------------------------------------