├── .gitignore ├── Freyr.plist ├── FreyrController.h ├── FreyrController.mm ├── FreyrForecast.h ├── FreyrForecast.mm ├── FreyrPreferences.h ├── FreyrPreferences.mm ├── Interfaces.h ├── Makefile ├── Preferences ├── FreyrDiscreteSliderTableCell.h ├── FreyrDiscreteSliderTableCell.m ├── FreyrHeaderCell.h ├── FreyrHeaderCell.m ├── FreyrRootListController.h ├── FreyrRootListController.m ├── FreyrSocialCell.h ├── FreyrSocialCell.m ├── Makefile ├── Resources │ ├── Info.plist │ ├── Root.plist │ ├── header@2x.png │ ├── header@3x.png │ ├── icon@2x.png │ ├── icon@3x.png │ ├── milo@2x.png │ ├── milo@3x.png │ ├── phil@2x.png │ └── phil@3x.png └── entry.plist ├── README.md ├── Tweak.xm ├── control ├── preview1.PNG ├── preview2.PNG ├── preview3.JPG ├── reset.sh └── setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.deb 2 | Packages/ 3 | .theos 4 | theos/ 5 | theos 6 | _/ 7 | obj/ 8 | 9 | 10 | -------------------------------------------------------------------------------- /Freyr.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.springboard" ); }; } 2 | -------------------------------------------------------------------------------- /FreyrController.h: -------------------------------------------------------------------------------- 1 | #import "Interfaces.h" 2 | 3 | @interface FreyrController : NSObject { 4 | CGPoint _panCoord; 5 | CGRect _originalRootFrame; 6 | CGRect _originalDockFrame; 7 | NSMutableArray* _verticalAdjacentBelowIcons; 8 | } 9 | @property (nonatomic, retain) SBIconView* performingIcon; 10 | @property (nonatomic, retain) UIView* forecastView; 11 | +(instancetype)sharedInstance; 12 | +(NSArray*)currentOrderedForecast; 13 | +(UIColor*)textColorForBlurStyle:(NSInteger)style; 14 | -(UIView*)viewForCurrentForecast; 15 | -(SBIconView*)retreivePerformingIcon; 16 | -(void)setupPerformingIcon; 17 | -(void)addForecastViewToPerformingIcon; 18 | -(void)removeForecastViewFromPerformingIcon; 19 | -(void)dismissFreyr; 20 | -(void)fullyOpenFreyr; 21 | @end -------------------------------------------------------------------------------- /FreyrController.mm: -------------------------------------------------------------------------------- 1 | #import "FreyrController.h" 2 | #import "FreyrForecast.h" 3 | #import 4 | #import "FreyrPreferences.h" 5 | 6 | #define kAnimationDuration 0.15 7 | 8 | @implementation FreyrController 9 | + (id)sharedInstance { 10 | static dispatch_once_t p = 0; 11 | __strong static id _sharedObject = nil; 12 | 13 | dispatch_once(&p, ^{ 14 | _sharedObject = [[self alloc] init]; 15 | }); 16 | 17 | return _sharedObject; 18 | } 19 | -(id)init { 20 | if ((self = [super init])) { 21 | SBDockIconListView* dockListView = [[objc_getClass("SBIconController") sharedInstance] dockListView]; 22 | _originalDockFrame = ((SBDockView*)[dockListView superview]).frame; 23 | _originalRootFrame = [[objc_getClass("SBIconController") sharedInstance] currentRootIconList].frame; 24 | } 25 | return self; 26 | } 27 | +(NSArray*)currentOrderedForecast { 28 | //TODO allow variable number of days 29 | return [[FreyrForecast sharedInstance] getForecastForNumberOfDays:[[FreyrPreferences sharedInstance] numberOfForecasts]]; 30 | } 31 | +(UIColor*)textColorForBlurStyle:(NSInteger)style { 32 | if (style == 2010) return [UIColor darkGrayColor]; 33 | return [UIColor whiteColor]; 34 | } 35 | -(UIView*)viewForCurrentForecast { 36 | CALL_ORIGIN; 37 | 38 | CGRect pFrame = _performingIcon.frame; 39 | UIView* holderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, pFrame.size.width, pFrame.size.height*3)]; 40 | //holderView.backgroundColor = [UIColor redColor]; 41 | 42 | //add blur view 43 | _UIBackdropView* backgroundView = [[_UIBackdropView alloc] initWithFrame:holderView.frame autosizesToFitSuperview:YES settings:[_UIBackdropViewSettings settingsForPrivateStyle:[[FreyrPreferences sharedInstance] blurStyle]]]; 44 | //backgroundView.layer = _performingIcon.layer; 45 | backgroundView.layer.cornerRadius = 15; 46 | backgroundView.layer.masksToBounds = YES; 47 | [holderView insertSubview:backgroundView atIndex:0]; 48 | 49 | //get current forcast 50 | NSArray* currentForecast = [FreyrController currentOrderedForecast]; 51 | NSLog(@"currentForecast: %@", currentForecast); 52 | 53 | CGFloat origin = _performingIcon.frame.size.width; 54 | for (NSNumber* averageTemp in currentForecast) { 55 | UILabel* tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, origin, pFrame.size.width, (holderView.frame.size.height - _performingIcon.frame.size.width)/currentForecast.count)]; 56 | tempLabel.textAlignment = NSTextAlignmentCenter; 57 | tempLabel.textColor = [FreyrController textColorForBlurStyle:[[FreyrPreferences sharedInstance] blurStyle]]; 58 | tempLabel.numberOfLines = 0; 59 | //tempLabel.lineBreakMode = UILineBreakModeWordWrap; 60 | tempLabel.text = [NSString stringWithFormat:@"%iº", [averageTemp intValue]]; 61 | 62 | [holderView addSubview:tempLabel]; 63 | 64 | NSLog(@"Label: %@", tempLabel); 65 | 66 | origin += tempLabel.frame.size.height; 67 | } 68 | 69 | holderView.tag = kForecastViewTag; 70 | 71 | return holderView; 72 | } 73 | 74 | -(void)calculateBelow2Icons { 75 | CALL_ORIGIN; 76 | 77 | //get vertically adjacent icons below it 78 | if (!_verticalAdjacentBelowIcons) { 79 | _verticalAdjacentBelowIcons = [[NSMutableArray alloc] init]; 80 | } 81 | 82 | [_verticalAdjacentBelowIcons removeAllObjects]; 83 | 84 | NSArray* apps = [[[objc_getClass("SBIconController") sharedInstance] currentFolderIconList] visibleIcons]; 85 | if (!apps) apps = [[[objc_getClass("SBIconController") sharedInstance] currentRootIconList] visibleIcons]; 86 | 87 | NSLog(@"apps: %@", apps); 88 | 89 | int wIndex = [apps indexOfObject:[(SBIconModel*)[[objc_getClass("SBIconController") sharedInstance] model] expectedIconForDisplayIdentifier:@"com.apple.weather"]]; 90 | int col = [objc_getClass("SBIconListView") iconColumnsForInterfaceOrientation:[[UIDevice currentDevice] orientation]]; 91 | 92 | //check if there exists an app 4 below this one 93 | if ((apps.count-1 > wIndex + col)) { 94 | SBIcon* below1 = [apps objectAtIndex:wIndex + col]; 95 | NSLog(@"below1: %@", below1); 96 | SBIconView* iconView = [[objc_getClass("SBIconViewMap") homescreenMap] mappedIconViewForIcon:below1]; 97 | if (!iconView) return; 98 | [_verticalAdjacentBelowIcons addObject:iconView]; 99 | 100 | if ((apps.count-1 > wIndex + col*2)) { 101 | SBIcon* below2 = [apps objectAtIndex:wIndex + col*2]; 102 | NSLog(@"below2: %@", below2); 103 | SBIconView* iconView = [[objc_getClass("SBIconViewMap") homescreenMap] mappedIconViewForIcon:below2]; 104 | if (!iconView) return; 105 | [_verticalAdjacentBelowIcons addObject:iconView]; 106 | } 107 | } 108 | } 109 | -(SBIconView*)retreivePerformingIcon { 110 | CALL_ORIGIN; 111 | 112 | SBIconController *controller = [objc_getClass("SBIconController") sharedInstance]; 113 | SBIcon *iconForOpeningApp = [controller.model expectedIconForDisplayIdentifier:@"com.apple.weather"]; 114 | 115 | SBIconViewMap *iconMap = [objc_getClass("SBIconViewMap") homescreenMap]; 116 | SBIconView *weatherIcon = [iconMap mappedIconViewForIcon:iconForOpeningApp]; 117 | 118 | NSLog(@"controller: %@", controller); 119 | NSLog(@"iconForOpeningApp: %@", iconForOpeningApp); 120 | NSLog(@"iconMap: %@", iconMap); 121 | NSLog(@"weatherIcon: %@", weatherIcon); 122 | 123 | _performingIcon = weatherIcon; 124 | 125 | return weatherIcon; 126 | } 127 | -(void)setupPerformingIcon { 128 | CALL_ORIGIN; 129 | 130 | //add the forecast view to the icon 131 | SBIconView* weatherIcon = [self retreivePerformingIcon]; 132 | 133 | [self addForecastViewToPerformingIcon]; 134 | 135 | if (![weatherIcon associatedGesture]) { 136 | UIPanGestureRecognizer* panRec = [[UIPanGestureRecognizer alloc] initWithTarget:[FreyrController sharedInstance] action:@selector(handlePan:)]; 137 | [weatherIcon setAssociatedGesture:panRec]; 138 | } 139 | 140 | [weatherIcon addGestureRecognizer:[weatherIcon associatedGesture]]; 141 | } 142 | -(void)addForecastViewToPerformingIcon { 143 | CALL_ORIGIN; 144 | 145 | if (!_performingIcon) { 146 | NSLog(@"Performing icon did not exist"); 147 | [self retreivePerformingIcon]; 148 | } 149 | 150 | [self removeForecastViewFromPerformingIcon]; 151 | 152 | [[FreyrForecast sharedInstance] calculateForecast]; 153 | [self calculateBelow2Icons]; 154 | 155 | //add it behind the icon 156 | _forecastView = [self viewForCurrentForecast]; 157 | _forecastView.clipsToBounds = YES; 158 | _forecastView.frame = CGRectMake(_performingIcon.frame.origin.x, _performingIcon.frame.origin.y, _performingIcon.frame.size.width, _performingIcon.frame.size.width); 159 | NSLog(@"_forecastView: %@", _forecastView); 160 | NSLog(@"_performingIcon: %@", _performingIcon); 161 | NSLog(@"[_performingIcon superview}: %@", [_performingIcon superview]); 162 | if (![_performingIcon superview]) return; 163 | [[_performingIcon superview] insertSubview:_forecastView belowSubview:_performingIcon]; 164 | } 165 | -(void)removeForecastViewFromPerformingIcon { 166 | while (UIView* view = [[_performingIcon superview] viewWithTag:kForecastViewTag]) { 167 | [view removeFromSuperview]; 168 | } 169 | } 170 | -(void)handlePan:(UIPanGestureRecognizer *)pan { 171 | if (pan.state == UIGestureRecognizerStateBegan) { 172 | [self calculateBelow2Icons]; 173 | 174 | _panCoord = [pan locationInView:_forecastView]; 175 | 176 | [UIView animateWithDuration:0.25 animations:^{ 177 | _performingIcon.iconLabelAlpha = 0.0; 178 | }]; 179 | } 180 | 181 | CGPoint newCoord = [pan locationInView:_forecastView]; 182 | CGFloat dY = (newCoord.y-_panCoord.y) / 4; 183 | 184 | CGFloat adjustedHeight = _forecastView.frame.size.height + dY; 185 | 186 | if (adjustedHeight > (_performingIcon.frame.size.height*3)) { 187 | adjustedHeight = _performingIcon.frame.size.height*3; 188 | } 189 | if (adjustedHeight < _performingIcon.frame.size.width) { 190 | adjustedHeight = _performingIcon.frame.size.width; 191 | } 192 | 193 | _forecastView.frame = CGRectMake(_performingIcon.frame.origin.x, _performingIcon.frame.origin.y, _performingIcon.frame.size.width, adjustedHeight); 194 | 195 | //if it goes off the bottom of the screen and it's in the dock, move the dock and screen up 196 | if ([_performingIcon isInDock]) { 197 | NSLog(@"_performingIcon was in dock"); 198 | //make sure no icons fade out 199 | [_verticalAdjacentBelowIcons removeAllObjects]; 200 | SBDockIconListView* dockListView = [[objc_getClass("SBIconController") sharedInstance] dockListView]; 201 | SBDockView* dockView = (SBDockView*)[dockListView superview]; 202 | 203 | NSLog(@"dockView: %@", dockView); 204 | 205 | //move the dock and the main icons up 206 | if (_forecastView.frame.origin.y + _forecastView.frame.size.height > (_originalDockFrame.size.height)) { 207 | //move the dock and the main icons up 208 | SBRootIconListView* rootIconList = [[objc_getClass("SBIconController") sharedInstance] currentRootIconList]; 209 | CGRect frame = rootIconList.frame; 210 | CGRect dockFrame = dockView.frame; 211 | 212 | NSLog(@"dockFrame: %@", NSStringFromCGRect(dockFrame)); 213 | 214 | dockView.frame = CGRectMake(dockFrame.origin.x, dockFrame.origin.y - ((_forecastView.frame.size.height + (_forecastView.frame.origin.y * 2)) - dockFrame.size.height), dockFrame.size.width, dockFrame.size.height + ((_forecastView.frame.size.height + (_forecastView.frame.origin.y * 2)) - dockFrame.size.height)); 215 | NSLog(@"dockView.frame.size.height: %f", dockView.frame.size.height); 216 | NSLog(@"_originalDockFrame.size.height: %f", _originalDockFrame.size.height); 217 | NSLog(@"(dockView.frame.size.height - _originalDockFrame.size.height): %f", (dockView.frame.size.height - _originalDockFrame.size.height)); 218 | rootIconList.frame = CGRectMake(frame.origin.x, _originalRootFrame.origin.y - (dockView.frame.size.height - _originalDockFrame.size.height), frame.size.width, frame.size.height); 219 | 220 | NSLog(@"dockFrame: %@", NSStringFromCGRect(dockView.frame)); 221 | } 222 | //else NSLog(@"forecastView was not too big for the dock"); 223 | } 224 | //if it goes off the bottom of the screen, move the screen up 225 | else if (_forecastView.frame.origin.y + _forecastView.frame.size.height > ([[objc_getClass("SBIconController") sharedInstance] currentRootIconList].frame.origin.y + [[objc_getClass("SBIconController") sharedInstance] currentRootIconList].frame.size.height)) { 226 | CGRect frame = [[objc_getClass("SBIconController") sharedInstance] currentRootIconList].frame; 227 | [[objc_getClass("SBIconController") sharedInstance] currentRootIconList].frame = CGRectMake(frame.origin.x, -((_forecastView.frame.origin.y + _forecastView.frame.size.height) - (frame.size.height)), frame.size.width, frame.size.height); 228 | } 229 | 230 | //fade apps below weather if necessary 231 | for (SBIconView* view in _verticalAdjacentBelowIcons) { 232 | if (_performingIcon.frame.origin.y + adjustedHeight > view.frame.origin.y) { 233 | [UIView animateWithDuration:kAnimationDuration animations:^{ 234 | view.alpha = 0.0; 235 | }]; 236 | } 237 | else { 238 | [UIView animateWithDuration:kAnimationDuration animations:^{ 239 | view.alpha = 1.0; 240 | }]; 241 | } 242 | } 243 | 244 | if (pan.state == UIGestureRecognizerStateEnded) { 245 | [self handleUntouchFromHeight:adjustedHeight]; 246 | } 247 | } 248 | -(void)handleUntouchFromHeight:(CGFloat)height { 249 | //if they've opened more than halfway down the 3-app total distance 250 | if (height > (_performingIcon.frame.size.height + _performingIcon.frame.size.height*1.5)) { 251 | //Fully open 252 | [self fullyOpenFreyr]; 253 | } 254 | else { 255 | //Close back up 256 | [self dismissFreyr]; 257 | } 258 | } 259 | -(void)dismissFreyr { 260 | [UIView animateWithDuration:kAnimationDuration animations:^{ 261 | CGRect frame = [[objc_getClass("SBIconController") sharedInstance] currentRootIconList].frame; 262 | //TODO find out if there is a scenario in which the y origin will not be 0 263 | [[objc_getClass("SBIconController") sharedInstance] currentRootIconList].frame = CGRectMake(frame.origin.x, 0, frame.size.width, frame.size.height); 264 | 265 | //change the dock back to its original frame 266 | SBDockIconListView* dockListView = [[objc_getClass("SBIconController") sharedInstance] dockListView]; 267 | SBDockView* dockView = (SBDockView*)[dockListView superview]; 268 | dockView.frame = _originalDockFrame; 269 | 270 | _forecastView.frame = CGRectMake(_performingIcon.frame.origin.x, _performingIcon.frame.origin.y, _performingIcon.frame.size.width, _performingIcon.frame.size.width); 271 | 272 | _performingIcon.iconLabelAlpha = 1.0; 273 | 274 | for (SBIconView* view in _verticalAdjacentBelowIcons) { 275 | view.alpha = 1.0; 276 | } 277 | }]; 278 | } 279 | -(void)fullyOpenFreyr { 280 | [UIView animateWithDuration:kAnimationDuration animations:^{ 281 | _forecastView.frame = CGRectMake(_performingIcon.frame.origin.x, _performingIcon.frame.origin.y, _performingIcon.frame.size.width, _performingIcon.frame.size.height * 3); 282 | 283 | _performingIcon.iconLabelAlpha = 0.0; 284 | 285 | for (SBIconView* view in _verticalAdjacentBelowIcons) { 286 | view.alpha = 0.0; 287 | } 288 | }]; 289 | } 290 | @end 291 | -------------------------------------------------------------------------------- /FreyrForecast.h: -------------------------------------------------------------------------------- 1 | #import "Interfaces.h" 2 | 3 | @interface FreyrForecast : NSObject 4 | @property (nonatomic, retain, setter=setDayForecasts:) NSArray* dayForecasts; 5 | @property (nonatomic, assign) CGFloat averageTemperature; 6 | +(id)sharedInstance; 7 | +(FreyrForecast*)forecastFromDayForecast:(id)dayForecast; 8 | -(void)calculateForecast; 9 | -(NSArray*)getForecastForNumberOfDays:(int)days; 10 | -(void)setDayForecasts:(NSArray*)dayForecasts; 11 | @end -------------------------------------------------------------------------------- /FreyrForecast.mm: -------------------------------------------------------------------------------- 1 | #import "FreyrForecast.h" 2 | #import "FreyrPreferences.h" 3 | 4 | @implementation FreyrForecast 5 | + (id)sharedInstance { 6 | static dispatch_once_t p = 0; 7 | __strong static id _sharedObject = nil; 8 | 9 | dispatch_once(&p, ^{ 10 | _sharedObject = [[self alloc] init]; 11 | }); 12 | 13 | return _sharedObject; 14 | } 15 | -(id)init { 16 | if ((self = [super init])) { 17 | _dayForecasts = [[NSArray alloc] init]; 18 | } 19 | return self; 20 | } 21 | +(FreyrForecast*)forecastFromDayForecast:(DayForecast*)dayForecast { 22 | FreyrForecast* forecast = [[FreyrForecast alloc] init]; 23 | forecast.averageTemperature = ([dayForecast.high floatValue]+ [dayForecast.low floatValue]) / 2; 24 | 25 | return forecast; 26 | } 27 | -(void)calculateForecast { 28 | WeatherPreferences* prefs = [objc_getClass("WeatherPreferences") sharedPreferences]; 29 | City* city = [prefs localWeatherCity]; 30 | 31 | NSArray* daysOrHours; 32 | 33 | if ([[FreyrPreferences sharedInstance] isDailyInterval]) { 34 | daysOrHours = city.dayForecasts; 35 | } 36 | else { 37 | daysOrHours = city.hourlyForecasts; 38 | } 39 | 40 | NSMutableArray* keys = [[NSMutableArray alloc] init]; 41 | 42 | //We start at index 1 because 0 is today 43 | for (int i = 1; i < daysOrHours.count; i++) { 44 | if ([[FreyrPreferences sharedInstance] isDailyInterval]) { 45 | DayForecast* fore = daysOrHours[i]; 46 | NSLog(@"day[%i]: %f", i, (([fore.high floatValue] + [fore.low floatValue])/2)); 47 | 48 | BOOL isCelsius = [[FreyrPreferences sharedInstance] isCelsius]; 49 | CGFloat celTemp = ([fore.high floatValue] + [fore.low floatValue])/2; 50 | CGFloat result = (isCelsius) ? celTemp : celTemp * 9/5 + 32; 51 | NSLog(@"result: %f", result); 52 | NSLog(@"celTemp * 9/5 + 32: %f", celTemp * 9/5 + 32); 53 | [keys addObject:@(result)]; 54 | } 55 | else { 56 | HourlyForecast* hour = daysOrHours[i]; 57 | BOOL isCelsius = [[FreyrPreferences sharedInstance] isCelsius]; 58 | CGFloat celTemp = [hour.detail floatValue]; 59 | CGFloat result = (isCelsius) ? celTemp : celTemp * 9/5 + 32; 60 | NSLog(@"result: %f", result); 61 | NSLog(@"celTemp * 9/5 + 32: %f", celTemp * 9/5 + 32); 62 | [keys addObject:@(result)]; 63 | } 64 | } 65 | 66 | [self setDayForecasts:[NSArray arrayWithArray:keys]]; 67 | } 68 | -(NSArray*)getForecastForNumberOfDays:(int)days { 69 | int count = _dayForecasts.count; 70 | 71 | if (days > count) days = count; 72 | 73 | return [_dayForecasts subarrayWithRange:NSMakeRange(0, days)]; 74 | } 75 | -(void)setDayForecasts:(NSArray*)dayForecasts { 76 | _dayForecasts = dayForecasts; 77 | } 78 | @end -------------------------------------------------------------------------------- /FreyrPreferences.h: -------------------------------------------------------------------------------- 1 | @interface FreyrPreferences : NSObject 2 | @property(nonatomic, readonly) BOOL isEnabled; 3 | @property(nonatomic, readonly) BOOL isCelsius; 4 | @property(nonatomic, readonly) BOOL isDailyInterval; 5 | @property(nonatomic, readonly) NSInteger blurStyle; 6 | @property(nonatomic, readonly) NSInteger numberOfForecasts; 7 | +(instancetype)sharedInstance; 8 | -(BOOL)boolForKey:(NSString *)key default:(BOOL)defaultVal; 9 | -(NSInteger)intForKey:(NSString *)key default:(NSInteger)defaultVal; 10 | @end -------------------------------------------------------------------------------- /FreyrPreferences.mm: -------------------------------------------------------------------------------- 1 | #import "FreyrPreferences.h" 2 | 3 | static NSString *const identifier = @"com.phillipt.freyr"; 4 | 5 | @implementation FreyrPreferences 6 | 7 | +(instancetype)sharedInstance { 8 | static dispatch_once_t p = 0; 9 | __strong static id _sharedObject = nil; 10 | 11 | dispatch_once(&p, ^{ 12 | _sharedObject = [[self alloc] init]; 13 | }); 14 | 15 | return _sharedObject; 16 | } 17 | 18 | -(instancetype)init { 19 | if (self=[super init]) { 20 | [self reloadPrefs]; 21 | } 22 | return self; 23 | } 24 | 25 | -(BOOL)boolForKey:(NSString *)key default:(BOOL)defaultVal { 26 | NSNumber *tempVal = (__bridge NSNumber *)CFPreferencesCopyAppValue((CFStringRef)key, (CFStringRef)identifier); 27 | return tempVal ? [tempVal boolValue] : defaultVal; 28 | } 29 | 30 | -(NSInteger)intForKey:(NSString *)key default:(NSInteger)defaultVal { 31 | NSNumber *tempVal = (__bridge NSNumber *)CFPreferencesCopyAppValue((CFStringRef)key, (CFStringRef)identifier); 32 | return tempVal ? [tempVal intValue] : defaultVal; 33 | } 34 | 35 | -(id)objectForKey:(NSString *)key default:(id)defaultVal { 36 | return (__bridge id)CFPreferencesCopyAppValue((CFStringRef)key, (CFStringRef)identifier) ?: defaultVal; 37 | } 38 | 39 | -(void)reloadPrefs { 40 | _isEnabled = [self boolForKey:@"enabled" default:YES]; 41 | _isCelsius = [[self objectForKey:@"isCelsius" default:@"fahrenheit"] isEqualToString:@"celsius"]; 42 | _blurStyle = [self intForKey:@"blurStyle" default:2060]; 43 | _isDailyInterval = [[self objectForKey:@"forecastTime" default:@"daily"] isEqualToString:@"daily"]; 44 | _numberOfForecasts = [self intForKey:@"forecastCount" default:4]; 45 | } 46 | 47 | @end 48 | 49 | static void reloadPrefs() { 50 | [[FreyrPreferences sharedInstance] reloadPrefs]; 51 | } 52 | 53 | static void __attribute__((constructor)) init() { 54 | CFNotificationCenterAddObserver (CFNotificationCenterGetDarwinNotifyCenter(), NULL, (CFNotificationCallback)reloadPrefs, (CFStringRef)[identifier stringByAppendingPathComponent:@"ReloadPrefs"], NULL, 0 ); 55 | } -------------------------------------------------------------------------------- /Interfaces.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #define kTweakName @"Freyr" 4 | 5 | #ifdef DEBUG 6 | #define NSLog(FORMAT, ...) NSLog(@"[%@: %s / %i] %@", kTweakName, __FILE__, __LINE__, [NSString stringWithFormat:FORMAT, ##__VA_ARGS__]) 7 | 8 | #else 9 | #define NSLog(FORMAT, ...) do {} while (0); 10 | #endif 11 | 12 | //#define CALL_ORIGIN NSLog(@"%s CALL_ORIGIN: [%@]", __PRETTY_FUNCTION__, [[[[NSThread callStackSymbols] objectAtIndex:1] componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"[]"]] objectAtIndex:1]) 13 | #define CALL_ORIGIN do {} while (0); 14 | 15 | extern "C" CFNotificationCenterRef CFNotificationCenterGetDistributedCenter(); 16 | 17 | #define kForecastViewTag 133742069 18 | 19 | @interface SBIcon : NSObject 20 | @property (nonatomic, retain) NSString* applicationBundleID; 21 | @end 22 | 23 | @interface SBIconView : UIView { 24 | UIView* _labelView; 25 | } 26 | @property (nonatomic, assign) CGFloat iconLabelAlpha; 27 | @property (nonatomic, retain) SBIcon* icon; 28 | -(CGRect)iconImageFrame; 29 | @end 30 | 31 | @interface DayForecast : NSObject 32 | @property (nonatomic, copy) NSString* high; //@synthesize high=_high - In the implementation block 33 | @property (nonatomic, copy) NSString* low; //@synthesize low=_low - In the implementation block //@synthesize icon=_icon - In the implementation block 34 | @property (assign, nonatomic) unsigned long long dayOfWeek; //@synthesize dayOfWeek=_dayOfWeek - In the implementation block 35 | @property (assign, nonatomic) unsigned long long dayNumber; 36 | @end 37 | 38 | @interface HourlyForecast : NSObject 39 | @property (nonatomic,copy) NSString * time; //@synthesize time=_time - In the implementation block 40 | @property (assign,nonatomic) long long hourIndex; //@synthesize hourIndex=_hourIndex - In the implementation block 41 | @property (nonatomic,copy) NSString * detail; //@synthesize detail=_detail - In the implementation block 42 | @end 43 | 44 | @interface City : NSObject 45 | @property (nonatomic, retain) NSArray* dayForecasts; 46 | @property (nonatomic, retain) NSArray* hourlyForecasts; 47 | -(void)update; 48 | @end 49 | 50 | @interface WeatherPreferences : NSObject 51 | +(id)sharedPreferences; 52 | -(City*)localWeatherCity; 53 | -(BOOL)isCelsius; 54 | @end 55 | 56 | @interface UIApplication (Private) 57 | -(BOOL)launchApplicationWithIdentifier:(NSString*)identifier suspended:(BOOL)suspended; 58 | @end 59 | 60 | @interface SBIconModel : NSObject 61 | -(NSDictionary*)iconState; 62 | -(SBIcon*)expectedIconForDisplayIdentifier:(NSString*)ident; 63 | @end 64 | 65 | @interface SBIconListView : UIView 66 | +(int)iconColumnsForInterfaceOrientation:(UIInterfaceOrientation)orient; 67 | @end 68 | 69 | @interface SBRootIconListView : SBIconListView 70 | -(NSArray*)visibleIcons; 71 | @end 72 | 73 | @interface SBDockIconListView : SBIconListView 74 | @end 75 | 76 | @interface SBDockView : UIView 77 | @end 78 | 79 | @interface SBIconController : NSObject 80 | @property (nonatomic, retain) SBIconModel* model; 81 | +(id)sharedInstance; 82 | -(SBRootIconListView*)currentRootIconList; 83 | -(SBRootIconListView*)currentFolderIconList; 84 | -(SBDockIconListView*)dockListView; 85 | @end 86 | 87 | @interface SBIconViewMap : NSObject 88 | @property (nonatomic, retain) SBIconModel* iconModel; 89 | +(id)homescreenMap; 90 | -(SBIconView*)mappedIconViewForIcon:(SBIcon*)icon; 91 | @end 92 | 93 | @interface _UIBackdropViewSettings : NSObject 94 | +(id)settingsForPrivateStyle:(int)style; 95 | @end 96 | 97 | @interface _UIBackdropView : UIView 98 | -(id)initWithFrame:(CGRect)frame autosizesToFitSuperview:(BOOL)resizes settings:(_UIBackdropViewSettings*)settings; 99 | @end 100 | 101 | @interface SBIconView (FreyrAdditions) 102 | -(void)setAssociatedGesture:(id)gesture; 103 | -(id)associatedGesture; 104 | -(BOOL)isInDock; 105 | @end 106 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO_EASY_ON_ME=1 2 | ARCHS = armv7 arm64 3 | #TARGET = iphone:clang:latest:latest 4 | THEOS_BUILD_DIR = Packages 5 | 6 | include theos/makefiles/common.mk 7 | 8 | TWEAK_NAME = Freyr 9 | Freyr_FILES = Tweak.xm 10 | Freyr_FILES += FreyrController.mm 11 | Freyr_FILES += FreyrForecast.mm 12 | Freyr_FILES += FreyrPreferences.mm 13 | Freyr_FRAMEWORKS = UIKit CoreGraphics QuartzCore 14 | Freyr_CFLAGS = -fobjc-arc 15 | 16 | include $(THEOS_MAKE_PATH)/tweak.mk 17 | 18 | after-install:: 19 | install.exec "killall -9 SpringBoard" 20 | SUBPROJECTS += Preferences 21 | 22 | include $(THEOS_MAKE_PATH)/aggregate.mk 23 | -------------------------------------------------------------------------------- /Preferences/FreyrDiscreteSliderTableCell.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface FreyrDiscreteSliderTableCell : PSSliderTableCell { } 6 | @end -------------------------------------------------------------------------------- /Preferences/FreyrDiscreteSliderTableCell.m: -------------------------------------------------------------------------------- 1 | #import "FreyrDiscreteSliderTableCell.h" 2 | #import 3 | #import 4 | 5 | @implementation FreyrDiscreteSliderTableCell 6 | 7 | -(id)initWithStyle:(UITableViewCellStyle)arg1 reuseIdentifier:(id)arg2 specifier:(PSSpecifier *)specifier { 8 | self = [super initWithStyle:arg1 reuseIdentifier:arg2 specifier:specifier]; 9 | if (self) { 10 | PSDiscreteSlider *slider = [[objc_getClass("PSDiscreteSlider") alloc] initWithFrame:CGRectZero]; 11 | 12 | [slider addTarget:specifier.target action:@selector(sliderMoved:) forControlEvents:UIControlEventAllTouchEvents]; 13 | [self setControl:slider]; 14 | 15 | } 16 | return self; 17 | } 18 | 19 | @end -------------------------------------------------------------------------------- /Preferences/FreyrHeaderCell.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | #import 5 | 6 | @interface FreyrHeaderCell : PSTableCell { 7 | UIImageView *headerView; 8 | } 9 | 10 | @end -------------------------------------------------------------------------------- /Preferences/FreyrHeaderCell.m: -------------------------------------------------------------------------------- 1 | #import "FreyrHeaderCell.h" 2 | 3 | @implementation FreyrHeaderCell 4 | 5 | -(instancetype)initWithSpecifier:(PSSpecifier *)specifier { 6 | if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"]) { 7 | headerView = [[UIImageView alloc] initWithImage:specifier.properties[@"iconImage"]]; 8 | [headerView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; 9 | [headerView setContentMode:UIViewContentModeScaleAspectFit]; 10 | [headerView setCenter:CGPointMake(headerView.center.x, headerView.center.y-30)]; 11 | [self addSubview:headerView]; 12 | } 13 | return self; 14 | } 15 | 16 | -(CGFloat)preferredHeightForWidth:(CGFloat)width { 17 | return 150; 18 | } 19 | 20 | @end -------------------------------------------------------------------------------- /Preferences/FreyrRootListController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FreyrRootListController : PSListController { 4 | PSSpecifier *sliderLabel; 5 | } 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /Preferences/FreyrRootListController.m: -------------------------------------------------------------------------------- 1 | #include "FreyrRootListController.h" 2 | #import 3 | 4 | static NSInteger specIndex; 5 | static PSSpecifier *sliderSpecifier; 6 | 7 | @implementation FreyrRootListController 8 | 9 | - (NSArray *)specifiers { 10 | if (!_specifiers) { 11 | _specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self]; 12 | specIndex = [_specifiers indexOfObject:[self specifierForID:@"SliderLabelCell"]]; 13 | sliderSpecifier = [self specifierForID:@"ForecastCountCell"]; 14 | int value = [[self readPreferenceValue:sliderSpecifier] intValue] ?: 4; 15 | sliderLabel = [PSSpecifier groupSpecifierWithHeader:@"Number of forecasts" footer:[NSString stringWithFormat:@"We'll show %d forecast%@", value, ((value == 1) ? @"" : @"s")]]; 16 | _specifiers[specIndex] = sliderLabel; 17 | } 18 | 19 | return _specifiers; 20 | } 21 | 22 | -(void)sliderMoved:(PSDiscreteSlider *)slider { 23 | 24 | sliderLabel = [PSSpecifier groupSpecifierWithHeader:@"Number of forecasts" footer:[NSString stringWithFormat:@"We'll show %d forecast%@", (int)slider.value, ((slider.value == 1) ? @"" : @"s")]]; 25 | _specifiers[specIndex] = sliderLabel; 26 | [self setPreferenceValue:@(slider.value) specifier:sliderSpecifier]; 27 | [self reloadSpecifierAtIndex:specIndex]; 28 | //[self reloadSpecifierAtIndex:specIndex+1]; 29 | 30 | } 31 | 32 | -(void)openTwitter:(PSSpecifier *)specifier { 33 | NSString *screenName = [specifier.properties[@"handle"] substringFromIndex:1]; //remove the "@" 34 | if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tweetbot:"]]) 35 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tweetbot:///user_profile/%@", screenName]]]; 36 | else if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"twitterrific:"]]) 37 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"twitterrific:///profile?screen_name=%@", screenName]]]; 38 | else if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tweetings:"]]) 39 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tweetings:///user?screen_name=%@", screenName]]]; 40 | else if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"twitter:"]]) 41 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"twitter://user?screen_name=%@", screenName]]]; 42 | else 43 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://mobile.twitter.com/%@", screenName]]]; 44 | } 45 | 46 | -(void)openReddit:(PSSpecifier *)specifier { 47 | NSString *screenName = specifier.properties[@"handle"]; 48 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[@"https://www.reddit.com" stringByAppendingPathComponent:screenName]]]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Preferences/FreyrSocialCell.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface FreyrSocialCell : PSTableCell { } 6 | @end -------------------------------------------------------------------------------- /Preferences/FreyrSocialCell.m: -------------------------------------------------------------------------------- 1 | #import "FreyrSocialCell.h" 2 | 3 | @implementation FreyrSocialCell 4 | 5 | -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier specifier:(PSSpecifier *)specifier { 6 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier specifier:specifier]; 7 | if (self) { 8 | self.detailTextLabel.text = specifier.properties[@"handle"]; 9 | self.detailTextLabel.textColor = [UIColor colorWithRed:151.0f/255.0f green:151.0f/255.0f blue:163.0f/255.0f alpha:1.0]; 10 | } 11 | return self; 12 | } 13 | 14 | -(void)layoutSubviews { 15 | [super layoutSubviews]; 16 | self.textLabel.textColor = [UIColor blackColor]; 17 | } 18 | 19 | @end -------------------------------------------------------------------------------- /Preferences/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS = armv7 arm64 2 | TARGET = iphone:clang:latest:latest 3 | 4 | include theos/makefiles/common.mk 5 | 6 | BUNDLE_NAME = Freyr 7 | Freyr_FILES = FreyrRootListController.m FreyrDiscreteSliderTableCell.m FreyrSocialCell.m FreyrHeaderCell.m 8 | Freyr_INSTALL_PATH = /Library/PreferenceBundles 9 | Freyr_FRAMEWORKS = UIKit CoreGraphics 10 | Freyr_PRIVATE_FRAMEWORKS = Preferences 11 | #Freyr_LIBRARIES = cephei 12 | Freyr_CFLAGS = -fobjc-arc 13 | 14 | include $(THEOS_MAKE_PATH)/bundle.mk 15 | 16 | internal-stage:: 17 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END) 18 | $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/Freyr.plist$(ECHO_END) 19 | -------------------------------------------------------------------------------- /Preferences/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | Freyr 9 | CFBundleIdentifier 10 | com.phillipt.freyr 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 | FreyrRootListController 23 | 24 | 25 | -------------------------------------------------------------------------------- /Preferences/Resources/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | cell 9 | PSGroupCell 10 | icon 11 | header.png 12 | headerCellClass 13 | FreyrHeaderCell 14 | 15 | 16 | cell 17 | PSSwitchCell 18 | defaults 19 | com.phillipt.freyr 20 | key 21 | enabled 22 | footerText 23 | Respring for this change to take effect 24 | label 25 | Enabled 26 | default 27 | 28 | id 29 | EnabledCell 30 | PostNotification 31 | com.phillipt.freyr/ReloadPrefs 32 | 33 | 34 | cell 35 | PSGroupCell 36 | 37 | 38 | cell 39 | PSSegmentCell 40 | defaults 41 | com.phillipt.freyr 42 | key 43 | isCelsius 44 | label 45 | Degrees 46 | default 47 | fahrenheit 48 | validTitles 49 | 50 | Fahrenheit 51 | Celsius 52 | 53 | validValues 54 | 55 | fahrenheit 56 | celsius 57 | 58 | id 59 | DegreesCell 60 | PostNotification 61 | com.phillipt.freyr/ReloadPrefs 62 | 63 | 64 | cell 65 | PSGroupCell 66 | 67 | 68 | cell 69 | PSLinkListCell 70 | detail 71 | PSListItemsController 72 | defaults 73 | com.phillipt.freyr 74 | key 75 | blurStyle 76 | label 77 | Blur Style 78 | default 79 | 2060 80 | validTitles 81 | 82 | Dark 83 | Common Blur 84 | CC Style 85 | Light 86 | 87 | validValues 88 | 89 | 1 90 | 2 91 | 2060 92 | 2010 93 | 94 | id 95 | BlurStyleCell 96 | PostNotification 97 | com.phillipt.freyr/ReloadPrefs 98 | 99 | 100 | cell 101 | PSGroupCell 102 | label 103 | Forecast Interval 104 | footerText 105 | Show either the daily or hourly forcast 106 | 107 | 108 | cell 109 | PSSegmentCell 110 | defaults 111 | com.phillipt.freyr 112 | key 113 | forecastTime 114 | label 115 | Forecast Interval 116 | default 117 | daily 118 | validTitles 119 | 120 | Hourly 121 | Daily 122 | 123 | validValues 124 | 125 | hourly 126 | daily 127 | 128 | id 129 | ForecastTimeCell 130 | PostNotification 131 | com.phillipt.freyr/ReloadPrefs 132 | 133 | 134 | cell 135 | PSGroupCell 136 | label 137 | Number of forecasts 138 | footerText 139 | We'll show 4 forecasts 140 | id 141 | SliderLabelCell 142 | 143 | 144 | 145 | cell 146 | PSSliderCell 147 | 148 | cellClass 149 | FreyrDiscreteSliderTableCell 150 | default 151 | 4 152 | defaults 153 | com.phillipt.freyr 154 | key 155 | forecastCount 156 | label 157 | Number of Forecasts 158 | max 159 | 6 160 | min 161 | 1 162 | PostNotification 163 | com.phillipt.freyr/ReloadPrefs 164 | id 165 | ForecastCountCell 166 | 167 | 168 | cell 169 | PSGroupCell 170 | label 171 | The Creators 172 | 173 | 174 | cell 175 | PSButtonCell 176 | action 177 | openTwitter: 178 | handle 179 | @phillipten 180 | label 181 | Phillip Tennen 182 | cellClass 183 | FreyrSocialCell 184 | icon 185 | phil.png 186 | 187 | 188 | cell 189 | PSButtonCell 190 | action 191 | openTwitter: 192 | handle 193 | @milo_darling 194 | label 195 | Milo Darling 196 | cellClass 197 | FreyrSocialCell 198 | icon 199 | milo.png 200 | 201 | 202 | title 203 | Freyr 204 | 205 | 206 | -------------------------------------------------------------------------------- /Preferences/Resources/header@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/Preferences/Resources/header@2x.png -------------------------------------------------------------------------------- /Preferences/Resources/header@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/Preferences/Resources/header@3x.png -------------------------------------------------------------------------------- /Preferences/Resources/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/Preferences/Resources/icon@2x.png -------------------------------------------------------------------------------- /Preferences/Resources/icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/Preferences/Resources/icon@3x.png -------------------------------------------------------------------------------- /Preferences/Resources/milo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/Preferences/Resources/milo@2x.png -------------------------------------------------------------------------------- /Preferences/Resources/milo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/Preferences/Resources/milo@3x.png -------------------------------------------------------------------------------- /Preferences/Resources/phil@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/Preferences/Resources/phil@2x.png -------------------------------------------------------------------------------- /Preferences/Resources/phil@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/Preferences/Resources/phil@3x.png -------------------------------------------------------------------------------- /Preferences/entry.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entry 6 | 7 | bundle 8 | Freyr 9 | cell 10 | PSLinkCell 11 | detail 12 | FreyrRootListController 13 | icon 14 | icon.png 15 | isController 16 | 17 | label 18 | Freyr 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Freyr 2 | ================= 3 | 4 | Freyr allows you to quickly check your Weather forecast from the homescreen, offering a variety of customization and personalization options, as well as allowing you to tailor exactly what info you would like to be displayed. 5 | 6 | Preview - Light | Preview - Dark | Settings 7 | :-------------------------:|:-------------------------:|:---------------------------: 8 | ![Preview](/preview1.PNG) | ![Preview](/preview2.PNG) | ![Settings](/preview3.JPG) 9 | 10 | Available on Cydia's BigBoss repo. 11 | 12 | By Phillip Tennen with additions by Milo Darling. 13 | 14 | GPL v3 license. Do whatever you want with this code, but provide attribution if you base your code off of it. 15 | -------------------------------------------------------------------------------- /Tweak.xm: -------------------------------------------------------------------------------- 1 | #import "Interfaces.h" 2 | #import "FreyrController.h" 3 | #import "FreyrForecast.h" 4 | #import "FreyrPreferences.h" 5 | 6 | %group Memes 7 | 8 | %hook SpringBoard 9 | -(void)applicationDidFinishLaunching:(id)application { 10 | %orig; 11 | 12 | WeatherPreferences* prefs = [%c(WeatherPreferences) sharedPreferences]; 13 | City* city = [prefs localWeatherCity]; 14 | 15 | //check if they don't have location services enabled 16 | if (!city) { 17 | NSLog(@"User did not have location services enabled for Weather.app"); 18 | [[[UIAlertView alloc] initWithTitle:@"Freyr" message:@"Please ensure you have location services enabled for the Weather app to use Freyr.\nYou can enable location services in Settings." delegate:nil cancelButtonTitle:@"Dismiss" otherButtonTitles:nil] show]; 19 | return; 20 | } 21 | 22 | [city update]; 23 | 24 | [[FreyrForecast sharedInstance] calculateForecast]; 25 | } 26 | %end 27 | 28 | %hook SBIconView 29 | %new 30 | -(void)setAssociatedGesture:(id)object { 31 | objc_setAssociatedObject(self, @selector(associatedGesture), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 32 | } 33 | %new 34 | -(id)associatedGesture { 35 | return objc_getAssociatedObject(self, @selector(associatedGesture)); 36 | } 37 | %end 38 | 39 | %hook SBFolderController 40 | -(BOOL)pushFolder:(id)arg1 animated:(BOOL)arg2 completion:(void(^)(void))arg3 { 41 | NSLog(@"pushFolder"); 42 | 43 | //remove it before they see it if it was already there 44 | [[FreyrController sharedInstance] removeForecastViewFromPerformingIcon]; 45 | [[FreyrController sharedInstance] dismissFreyr]; 46 | 47 | //custom completion block 48 | void (^cust)(void) = ^void(void){ 49 | arg3(); 50 | 51 | [[FreyrController sharedInstance] setupPerformingIcon]; 52 | }; 53 | 54 | return %orig(arg1, arg2, cust); 55 | } 56 | -(void)unscatterAnimated:(BOOL)arg1 afterDelay:(double)arg2 withCompletion:(void(^)(void))arg3 { 57 | //%log; 58 | 59 | //remove it before they see it if it was already there 60 | [[FreyrController sharedInstance] removeForecastViewFromPerformingIcon]; 61 | [[FreyrController sharedInstance] dismissFreyr]; 62 | 63 | //custom completion block 64 | void (^cust)(void) = ^void(void){ 65 | arg3(); 66 | 67 | [[FreyrController sharedInstance] setupPerformingIcon]; 68 | }; 69 | 70 | %orig(arg1, arg2, cust); 71 | } 72 | %end 73 | 74 | %hook SBIconController 75 | -(void)setIsEditing:(BOOL)editing { 76 | %log; 77 | %orig; 78 | 79 | SBIconView* weatherIcon = [[FreyrController sharedInstance] retreivePerformingIcon]; 80 | [[FreyrController sharedInstance] dismissFreyr]; 81 | if (!weatherIcon) { 82 | NSLog(@"Icon could not be retreived."); 83 | return; 84 | } 85 | NSLog(@"weatherIcon: %@", weatherIcon); 86 | 87 | if (editing) { 88 | [weatherIcon removeGestureRecognizer:[weatherIcon associatedGesture]]; 89 | 90 | [[FreyrController sharedInstance] removeForecastViewFromPerformingIcon]; 91 | } 92 | else { 93 | if (![weatherIcon associatedGesture]) { 94 | NSLog(@"Associated gesture did not exist. Creating."); 95 | UIPanGestureRecognizer* panRec = [[UIPanGestureRecognizer alloc] initWithTarget:[FreyrController sharedInstance] action:@selector(handlePan:)]; 96 | [weatherIcon setAssociatedGesture:panRec]; 97 | } 98 | 99 | if ([[weatherIcon gestureRecognizers] containsObject:[weatherIcon associatedGesture]]) { 100 | NSLog(@"weatherIcon contained gesture, removing it."); 101 | [weatherIcon removeGestureRecognizer:[weatherIcon associatedGesture]]; 102 | } 103 | 104 | [weatherIcon addGestureRecognizer:[weatherIcon associatedGesture]]; 105 | 106 | [[FreyrController sharedInstance] addForecastViewToPerformingIcon]; 107 | } 108 | } 109 | %end 110 | 111 | /* 112 | %hook SBLockScreenManager 113 | -(void) _finishUIUnlockFromSource:(int)source withOptions:(id)options { 114 | //remove it before they see it if it was already there 115 | [[FreyrController sharedInstance] removeForecastViewFromPerformingIcon]; 116 | 117 | %orig; 118 | } 119 | 120 | -(void)_sendUILockStateChangedNotification { 121 | //remove it before they see it if it was already there 122 | [[FreyrController sharedInstance] removeForecastViewFromPerformingIcon]; 123 | 124 | %orig; 125 | } 126 | -(void)_deviceLockedChanged:(id)arg1 { 127 | //remove it before they see it if it was already there 128 | [[FreyrController sharedInstance] removeForecastViewFromPerformingIcon]; 129 | 130 | [[FreyrController sharedInstance] setupPerformingIcon]; 131 | 132 | %orig; 133 | } 134 | 135 | %end 136 | */ 137 | %end 138 | 139 | %ctor { 140 | if ([[FreyrPreferences sharedInstance] isEnabled]) %init(Memes); 141 | 142 | if ([[NSBundle mainBundle].bundleIdentifier isEqualToString:@"com.apple.springboard"]) { 143 | dlopen("System/Library/PrivateFrameworks/Weather.framework/Weather", RTLD_NOW); 144 | } 145 | } -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.phillipt.freyr 2 | Name: Freyr 3 | Depends: mobilesubstrate 4 | Version: 0.0.1 5 | Architecture: iphoneos-arm 6 | Description: At-a-glance forecasts with the swipe of a finger 7 | Maintainer: Phillip Tennen 8 | Author: Phillip Tennen 9 | Section: Tweaks 10 | -------------------------------------------------------------------------------- /preview1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/preview1.PNG -------------------------------------------------------------------------------- /preview2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/preview2.PNG -------------------------------------------------------------------------------- /preview3.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codyd51/Freyr/175deb87532dcda46d8252f9034aba58ef6ae668/preview3.JPG -------------------------------------------------------------------------------- /reset.sh: -------------------------------------------------------------------------------- 1 | # for reseting version number so the next build is *-1 2 | make clean 3 | rm .theos/packages/* 4 | rm .theos/last_package 5 | echo "You're good to go! Just change the version number in your control file to whatever you want!" 6 | exit 0 7 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | # For setting up after cloning from GitHub (getting theos links good) 2 | rm ./theos 3 | ln -s $THEOS ./theos 4 | # Please not that the next part only works when there's one subproject (e.g. a preference bundle) 5 | SUBPROJECTS=`cat Makefile | grep 'SUBPROJECTS' | sed -e 's/.*SUBPROJECTS += //'` 6 | rm ./$SUBPROJECTS/theos 7 | ln -s $THEOS ./$SUBPROJECTS/theos 8 | exit 0 --------------------------------------------------------------------------------