├── .gitignore ├── HomemadeBread.plist ├── control ├── Makefile ├── README.md ├── LAListener.h └── Tweak.xm /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.stamp 3 | .theos 4 | .DS_Store 5 | theos 6 | _ 7 | obj 8 | debs -------------------------------------------------------------------------------- /HomemadeBread.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.springboard" ); }; } 2 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: org.thebigboss.homemadebread 2 | Name: Homemade Bread 3 | Depends: mobilesubstrate 4 | Version: 0.0.1 5 | Architecture: iphoneos-arm 6 | Description: Use the home button to activate a breadcrumb 7 | Maintainer: twodayslate 8 | Author: twodayslate 9 | Section: Tweaks 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | THEOS_DEVICE_IP = 127.0.0.1 2 | THEOS_DEVICE_PORT = 2222 3 | 4 | include $(THEOS)/makefiles/common.mk 5 | 6 | TWEAK_NAME = HomemadeBread 7 | HomemadeBread_FILES = Tweak.xm 8 | 9 | include $(THEOS_MAKE_PATH)/tweak.mk 10 | 11 | after-install:: 12 | install.exec "killall -9 SpringBoard" 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Homemade Bread 2 | 3 | Most up to date description and pricing can be found on the BigBoss repository. 4 | 5 | Using kirb/theos 6 | 7 | This package is created under the "Attribution-NonCommercial-ShareAlike 4.0 International" license. 8 | 9 | Donations via [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2R9WDZCE7CPZ8) or [Bitcoin](https://coinbase.com/checkouts/59ead722b181591150e7de4ed6769cb4) are welcomed! 10 | 11 | There is no warranty for this product and comes as is. @twodayslate is not responsible for any damage that this product may cause. -------------------------------------------------------------------------------- /LAListener.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | // Listeners represent specific actions that can be performed in response to an event 5 | // Must be registered with LAActivator inside SpringBoard via the registerListener:forName: method 6 | 7 | @class LAActivator, LAEvent, UIImage; 8 | 9 | @protocol LAListener 10 | @optional 11 | 12 | - (void)activator:(LAActivator *)activator didChangeToEventMode:(NSString *)eventMode; 13 | 14 | // Incoming events 15 | - (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event forListenerName:(NSString *)listenerName; // Normal assigned events 16 | - (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event forListenerName:(NSString *)listenerName; // Sent when a chorded event gets escalated (short hold becoems a long hold, for example) 17 | - (BOOL)activator:(LAActivator *)activator receiveUnlockingDeviceEvent:(LAEvent *)event forListenerName:(NSString *)listenerName; // Sent at the lock screen when listener is not compatible with event, but potentially is able to unlock the screen to handle it 18 | - (void)activator:(LAActivator *)activator receiveDeactivateEvent:(LAEvent *)event; // Sent when the menu button is pressed. Only handle if you want to suppress the standard menu button behaviour! 19 | - (void)activator:(LAActivator *)activator otherListenerDidHandleEvent:(LAEvent *)event; // Sent when another listener has handled the event 20 | - (void)activator:(LAActivator *)activator receivePreviewEventForListenerName:(NSString *)listenerName; // Sent from the settings pane when a listener is assigned 21 | 22 | // Simpler versions 23 | - (void)activator:(LAActivator *)activator receiveEvent:(LAEvent *)event; 24 | - (void)activator:(LAActivator *)activator abortEvent:(LAEvent *)event; 25 | 26 | // Metadata (may be cached) 27 | - (NSString *)activator:(LAActivator *)activator requiresLocalizedTitleForListenerName:(NSString *)listenerName; 28 | - (NSString *)activator:(LAActivator *)activator requiresLocalizedDescriptionForListenerName:(NSString *)listenerName; 29 | - (NSString *)activator:(LAActivator *)activator requiresLocalizedGroupForListenerName:(NSString *)listenerName; 30 | - (NSNumber *)activator:(LAActivator *)activator requiresRequiresAssignmentForListenerName:(NSString *)listenerName; 31 | - (NSArray *)activator:(LAActivator *)activator requiresCompatibleEventModesForListenerWithName:(NSString *)listenerName; 32 | - (NSNumber *)activator:(LAActivator *)activator requiresIsCompatibleWithEventName:(NSString *)eventName listenerName:(NSString *)listenerName; 33 | - (NSArray *)activator:(LAActivator *)activator requiresExclusiveAssignmentGroupsForListenerName:(NSString *)listenerName; 34 | - (id)activator:(LAActivator *)activator requiresInfoDictionaryValueOfKey:(NSString *)key forListenerWithName:(NSString *)listenerName; 35 | - (BOOL)activator:(LAActivator *)activator requiresNeedsPoweredDisplayForListenerName:(NSString *)listenerName; 36 | 37 | // Icons 38 | // Fast path that supports scale 39 | - (NSData *)activator:(LAActivator *)activator requiresIconDataForListenerName:(NSString *)listenerName scale:(CGFloat *)scale; 40 | - (NSData *)activator:(LAActivator *)activator requiresSmallIconDataForListenerName:(NSString *)listenerName scale:(CGFloat *)scale; 41 | // Legacy 42 | - (NSData *)activator:(LAActivator *)activator requiresIconDataForListenerName:(NSString *)listenerName; 43 | - (NSData *)activator:(LAActivator *)activator requiresSmallIconDataForListenerName:(NSString *)listenerName; 44 | // For cases where PNG data isn't available quickly 45 | - (UIImage *)activator:(LAActivator *)activator requiresIconForListenerName:(NSString *)listenerName scale:(CGFloat)scale; 46 | - (UIImage *)activator:(LAActivator *)activator requiresSmallIconForListenerName:(NSString *)listenerName scale:(CGFloat)scale; 47 | 48 | @end 49 | 50 | @interface LAActivator 51 | +(LAActivator *)sharedInstance; 52 | -(id)hasSeenListenerWithName:(id)arg1; 53 | -(id)assignEvent:(id)arg1 toListenerWithName:(id)arg2; 54 | -(id)registerListener:(id)arg1 forName:(id)arg2; 55 | @end 56 | 57 | @interface LAEvent 58 | +(id)eventWithName:(id)arg1; 59 | -(id)setHandled:(BOOL)arg1; 60 | @end -------------------------------------------------------------------------------- /Tweak.xm: -------------------------------------------------------------------------------- 1 | #include "LAListener.h" 2 | #include "substrate.h" 3 | 4 | @interface UISystemNavigationAction 5 | - (id)destinations; 6 | - (int)UIActionType; 7 | - (id)titleForDestination:(unsigned int)arg1; 8 | - (id)bundleIdForDestination:(unsigned int)arg1; 9 | - (id)URLForDestination:(unsigned int)arg1; 10 | @end 11 | 12 | @interface UISystemNavigationActionDestinationContext 13 | - (id)debugDescription; 14 | @end 15 | 16 | @interface SBMainDisplaySceneManager 17 | @property(retain, nonatomic) UISystemNavigationAction *currentBreadcrumbNavigationAction; 18 | - (void)_presentSpotlightFromEdge:(unsigned long long)arg1 fromBreadcrumb:(_Bool)arg2; 19 | - (void)_activateBreadcrumbApplication:(id)arg1; 20 | @end 21 | 22 | @interface SBWorkspaceApplication 23 | @property(retain, nonatomic) id application; // @synthesize application=_application; 24 | @end 25 | 26 | 27 | static SBMainDisplaySceneManager *currentSceneManager = nil; 28 | static NSArray *whitelist = @[@"com.apple.mobilesms.compose", @"com.apple.MailCompositionService"]; 29 | 30 | %hook SBMainDisplaySceneManager 31 | - (_Bool)_shouldBreadcrumbApplication:(SBWorkspaceApplication *)arg1 withTransitionContext:(id)arg2 { 32 | // returns false for a mail/sms composer view controller 33 | // so need to still save the breadcrumb anyways 34 | // if someone can show me when this "app" is launched and it 35 | // is not for a view controller, please let me know 36 | if(currentSceneManager) { 37 | if([whitelist containsObject:[arg1.application bundleIdentifier]]) { 38 | HBLogDebug(@"Still gonna save, cause just presenting the mail composer"); 39 | currentSceneManager = self; 40 | } else { 41 | currentSceneManager = nil; 42 | } 43 | } 44 | 45 | // So this is the importatnt piece. 46 | // However, the destination for the scene manager does not change 47 | // even when there is no breadcrumb 48 | // there is also always a destination, even when there is no breadcrumb 49 | // I reset the variable, just in case it gets released somehow, but 50 | // this scenemanager is pretty consistant 51 | currentSceneManager = %orig ? self : currentSceneManager; 52 | return %orig; 53 | } 54 | - (_Bool)_isActivatingPinnedBreadcrumbApp:(id)arg1 withTransitionContext:(id)arg2 { 55 | %log; 56 | return %orig; 57 | } 58 | - (id)_breadcrumbNavigationActionForApplication:(id)arg1 withTransitionContext:(id)arg2{ 59 | %log; 60 | return %orig; 61 | } 62 | - (void)_activateAppLink:(id)arg1 withAppLinkState:(id)arg2 transitionContext:(id)arg3 wasFromSpotlight:(_Bool)arg4 previousBreadcrumb:(id)arg5 { 63 | %log; 64 | %orig; 65 | } 66 | - (void)_activateBreadcrumbApplication:(id)arg1 { 67 | %log; 68 | %orig; 69 | } 70 | - (id)_breadcrumbBundleIdForApplication:(id)arg1 withTransitionContext:(id)arg2 { 71 | %log; 72 | return %orig; 73 | } 74 | - (void)_presentSpotlightFromEdge:(unsigned long long)arg1 fromBreadcrumb:(_Bool)arg2 { 75 | %log; 76 | %orig; 77 | } 78 | - (void)_deviceOrientationChanged:(id)arg1 { 79 | %log; 80 | %orig; 81 | } 82 | - (void)_application:(id)arg1 initiatedChangefromInterfaceOrientation:(long long)arg2 toInterfaceOrientation:(long long)arg3 scene:(id)arg4 sceneSettings:(id)arg5 transitionContext:(id)arg6 { 83 | %log; 84 | %orig; 85 | } 86 | - (id)_rotationAnimationSettingsForRotationFromInterfaceOrientation:(long long)arg1 toInterfaceOrientation:(long long)arg2 medusaSettings:(id)arg3 { 87 | %log; 88 | return %orig; 89 | } 90 | - (_Bool)_handleAction:(id)arg1 forScene:(id)arg2 { 91 | %log; 92 | return %orig; 93 | } 94 | - (id)_applicationForAppLink:(id)arg1 { 95 | %log; 96 | return %orig; 97 | } 98 | 99 | %end 100 | 101 | @interface SBUIController 102 | - (void)activateApplication:(id)arg1; 103 | @end 104 | 105 | @interface SBIconController 106 | - (_Bool)presentSpotlightFromEdge:(unsigned long long)arg1 fromBreadcrumb:(_Bool)arg2 animated:(_Bool)arg3; 107 | - (_Bool)_presentTopEdgeSpotlight:(_Bool)arg1; 108 | @end 109 | 110 | %hook UIStatusBarBreadcrumbItemView 111 | - (void)userDidActivateButton:(id)arg1 { 112 | %log; 113 | %orig; 114 | } 115 | %end 116 | 117 | %hook UIApplication 118 | - (void)_clearSystemNavigationAction { 119 | %log; 120 | %orig; 121 | } 122 | - (void)_setSystemNavigationAction:(id)arg1 { 123 | %log; 124 | %orig; 125 | } 126 | %end 127 | 128 | %hook UIStatusBarSystemNavigationItemView 129 | - (void)userDidActivateButton:(id)arg1 { 130 | %log; 131 | %orig; 132 | } 133 | - (void)setButton:(id)arg1 { 134 | %log; 135 | %orig; 136 | } 137 | %end 138 | 139 | %hook UIStatusBarServer 140 | + (void)addStatusBarItem:(int)arg1 { 141 | %log; 142 | %orig; 143 | } 144 | + (void)removeStatusBarItem:(int)arg1 { 145 | %log; 146 | %orig; 147 | } 148 | %end 149 | @interface UIApplication (extras) 150 | - (id)_systemNavigationAction; 151 | - (BOOL)launchApplicationWithIdentifier:(id)arg1 suspended:(BOOL)arg2; 152 | - (BOOL)openURL:(id)arg1; 153 | - (id)statusBar; 154 | @end 155 | 156 | @interface SBApplication 157 | - (void)activate; 158 | @end 159 | 160 | @interface SBApplicationController 161 | + (SBApplicationController *)sharedInstance; 162 | - (id)applicationWithBundleIdentifier:(id)arg1; 163 | @end 164 | 165 | @interface SlideBackActivator : NSObject 166 | @end 167 | 168 | @interface UIView (extras) 169 | -(id)recursiveDescription; 170 | @end 171 | 172 | @interface SBSearchGesture 173 | -(void)revealAnimated:(BOOL)arg1; 174 | @end 175 | 176 | %hook SBUIController 177 | - (void)activateApplication:(id)arg1 { 178 | %log; 179 | %orig; 180 | } 181 | %end 182 | 183 | 184 | 185 | // struct customStruct { 186 | // BOOL itemIsEnabled[27]; 187 | // BOOL timeString[64]; 188 | // int gsmSignalStrengthRaw; 189 | // int gsmSignalStrengthBars; 190 | // BOOL serviceString[100]; 191 | // BOOL serviceCrossfadeString[100]; 192 | // BOOL serviceImages[2][100]; 193 | // BOOL operatorDirectory[1024]; 194 | // unsigned int serviceContentType; 195 | // int wifiSignalStrengthRaw; 196 | // int wifiSignalStrengthBars; 197 | // unsigned int dataNetworkType; 198 | // int batteryCapacity; 199 | // unsigned int batteryState; 200 | // BOOL batteryDetailString[150]; 201 | // int bluetoothBatteryCapacity; 202 | // int thermalColor; 203 | // unsigned int thermalSunlightMode : 1; 204 | // unsigned int slowActivity : 1; 205 | // unsigned int syncActivity : 1; 206 | // BOOL activityDisplayId[256]; 207 | // unsigned int bluetoothConnected : 1; 208 | // unsigned int displayRawGSMSignal : 1; 209 | // unsigned int displayRawWifiSignal : 1; 210 | // unsigned int locationIconType : 1; 211 | // unsigned int quietModeInactive : 1; 212 | // unsigned int tetheringConnectionCount; 213 | // unsigned int batterySaverModeActive : 1; 214 | // BOOL breadcrumbTitle[256]; // I would assume this would be an NSString * but I don't understand why it would be 256 215 | // BOOL breadcrumbSecondaryTitle[256]; 216 | // }; 217 | 218 | static BOOL tryBreadcrumbLaunch() { 219 | if(currentSceneManager) { 220 | 221 | HBLogDebug(@"SBMainStatusBarStateProvider = %@", [%c(SBMainStatusBarStateProvider) sharedInstance]); 222 | // The breadcrumb view is not in UIStatusBar view because it is actually in a UIStatusBarForegroundView 223 | // and then inside a UIStatusBarBreadcrumbItemView 224 | // can't see this while hooking SpringBoard and I don't want to hook UIKit. Am I doing this wrong? 225 | // UIStatusBar *statusBar = [[%c(SpringBoard) sharedApplication] statusBar]; 226 | // struct customStruct raw = MSHookIvar(statusBar, "_currentRawData"); 227 | // I'd love for someone to figure out this struct 228 | //HBLogDebug(@"raw Data = %@", (NSString *)raw.breadcrumbTitle); 229 | //HBLogDebug(@"statusbar = %@", [(UIView *)[[%c(SpringBoard) sharedApplication] statusBar] recursiveDescription]); 230 | 231 | HBLogDebug(@"current system navigation action = %@", [[%c(SpringBoard) sharedApplication] _systemNavigationAction]); // why is this always nil? 232 | //HBLogDebug(@"current scene manger = %@", currentSceneManager) 233 | HBLogDebug(@"UIActionType = %d", [currentSceneManager.currentBreadcrumbNavigationAction UIActionType]); // 18 234 | if(currentSceneManager.currentBreadcrumbNavigationAction) { 235 | HBLogDebug(@"currentBreadcrumbNavigationAction = %@", currentSceneManager.currentBreadcrumbNavigationAction); 236 | HBLogDebug(@"destinations = %@", currentSceneManager.currentBreadcrumbNavigationAction.destinations); 237 | for(id dest in currentSceneManager.currentBreadcrumbNavigationAction.destinations) { 238 | HBLogInfo(@"%@ %@ with bundleID %@ or url %@", dest, [currentSceneManager.currentBreadcrumbNavigationAction titleForDestination:(int)(NSInteger)dest], 239 | [currentSceneManager.currentBreadcrumbNavigationAction bundleIdForDestination:(int)(NSInteger)dest], 240 | [currentSceneManager.currentBreadcrumbNavigationAction URLForDestination:(int)(NSInteger)dest]); 241 | } 242 | if(currentSceneManager.currentBreadcrumbNavigationAction.destinations && [currentSceneManager.currentBreadcrumbNavigationAction.destinations count]) { 243 | if([currentSceneManager.currentBreadcrumbNavigationAction bundleIdForDestination:0]) { 244 | HBLogDebug(@"it is a id"); 245 | NSString *displayID = [currentSceneManager.currentBreadcrumbNavigationAction bundleIdForDestination:0]; 246 | 247 | SBApplicationController *appController = (SBApplicationController *)[%c(SBApplicationController) sharedInstance]; 248 | SBApplication *app = [appController applicationWithBundleIdentifier:displayID]; 249 | // // [app activate]; 250 | // [(SBUIController *)[%c(SBUIController) sharedInstance] activateApplication:app]; 251 | 252 | if([displayID isEqualToString:@"com.apple.springboard.spotlight-placeholder"]) { 253 | // For some reason this doesn't want to display spotlight - even though 254 | // this is the right call, must be missing something 255 | [currentSceneManager _presentSpotlightFromEdge:1 fromBreadcrumb:1]; 256 | //[(SBIconController *)[%c(SBIconController) sharedInstance] presentSpotlightFromEdge:1 fromBreadcrumb:1 animated:NO]; 257 | //[(SBIconController *)[%c(SBIconController) sharedInstance] _presentTopEdgeSpotlight:NO]; 258 | //[(SBSearchGesture *)[%c(SBSearchGesture) sharedInstance] revealAnimated:YES]; 259 | currentSceneManager = nil; 260 | return YES; 261 | } 262 | 263 | [currentSceneManager _activateBreadcrumbApplication:app]; 264 | currentSceneManager = nil; 265 | //[[%c(SpringBoard) sharedApplication] launchApplicationWithIdentifier:displayID suspended:NO]; 266 | return YES; 267 | } else if ([currentSceneManager.currentBreadcrumbNavigationAction URLForDestination:0]) { 268 | HBLogDebug(@"it is a url"); 269 | currentSceneManager = nil; 270 | [[%c(SpringBoard) sharedApplication] openURL:[currentSceneManager.currentBreadcrumbNavigationAction URLForDestination:0]]; 271 | return YES; 272 | } 273 | } 274 | } 275 | } 276 | return NO; 277 | } 278 | 279 | %hook SpringBoard 280 | - (void)_handleMenuButtonEvent { 281 | %log; 282 | Class la = objc_getClass("LAActivator"); 283 | if (!la) { 284 | HBLogDebug(@"Activator not installed so hooking menu button event"); 285 | if(!tryBreadcrumbLaunch()) { 286 | %orig; 287 | } 288 | } else { 289 | %orig; 290 | } 291 | } 292 | %end 293 | 294 | @implementation SlideBackActivator 295 | - (void)activator:(id)activator receiveEvent:(id)event { 296 | HBLogDebug(@"launching with activator"); 297 | [event setHandled:tryBreadcrumbLaunch()]; 298 | } 299 | - (NSString *)activator:(LAActivator *)activator requiresLocalizedTitleForListenerName:(NSString *)listenerName { 300 | return @"Homemade Bread"; 301 | } 302 | - (NSString *)activator:(LAActivator *)activator requiresLocalizedDescriptionForListenerName:(NSString *)listenerName { 303 | return @"Breadcrumb-aware home button"; 304 | } 305 | 306 | - (id)activator:(LAActivator *)activator requiresInfoDictionaryValueOfKey:(NSString *)key forListenerWithName:(NSString *)listenerName { 307 | if([key isEqualToString:@"receives-raw-events"]) { 308 | return [NSNumber numberWithBool:YES]; 309 | } 310 | return nil; 311 | } 312 | 313 | @end 314 | 315 | 316 | 317 | 318 | %ctor { 319 | dlopen("/usr/lib/libactivator.dylib", RTLD_LAZY); 320 | 321 | static SlideBackActivator *listener = [[SlideBackActivator alloc] init]; 322 | 323 | 324 | // TODO: put this in the init function 325 | id la = [%c(LAActivator) sharedInstance]; 326 | if ([la respondsToSelector:@selector(hasSeenListenerWithName:)] && [la respondsToSelector:@selector(assignEvent:toListenerWithName:)]) { 327 | if (![la hasSeenListenerWithName:@"org.thebigboss.homemadebread"]) { 328 | [la assignEvent:[%c(LAEvent) eventWithName:@"libactivator.menu.press.single"] toListenerWithName:@"org.thebigboss.homemadebread"]; 329 | } 330 | } 331 | 332 | // register our listener. do this after the above so it still hasn't "seen" us if this is first launch 333 | [(LAActivator*)[%c(LAActivator) sharedInstance] registerListener:listener forName:@"org.thebigboss.homemadebread"]; // can also be done in +load https://github.com/nickfrey/NowNow/blob/master/Tweak.xm#L31 334 | } --------------------------------------------------------------------------------