-
Notifications
You must be signed in to change notification settings - Fork 8
/
Clock.xm
185 lines (161 loc) · 8.54 KB
/
Clock.xm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include "Neon.h"
#include <AppSupport/CPDistributedMessagingCenter.h>
@interface SBHClockHandsImageSet
@property (nonatomic, readwrite, strong) UIImage *seconds;
@property (nonatomic, readwrite, strong) UIImage *minutes;
@property (nonatomic, readwrite, strong) UIImage *hours;
@property (nonatomic, readwrite, strong) UIImage *hourMinuteDot;
@property (nonatomic, readwrite, strong) UIImage *secondDot;
+ (SBHClockHandsImageSet *)makeImageSetForMetrics:(SBHClockApplicationIconImageMetrics *)metrics;
@end
@interface SBClockApplicationIconImageView : UIView
@property (assign, nonatomic) BOOL showsSquareCorners;
- (void)_setAnimating:(BOOL)animating;
+ (instancetype)sharedInstance;
+ (CALayer *)makeSecondsHandLayerWithImageSet:(SBHClockHandsImageSet *)imageSet;
- (void)applyMetrics:(SBHClockApplicationIconImageMetrics)metrics;
@end
NSArray *themes;
NSString *overrideTheme;
UIImage *clockImageNamed(NSString *name) {
if (overrideTheme) {
NSString *path;
if ([overrideTheme rangeOfString:@"/"].location != NSNotFound && [name isEqualToString:@"ClockIconBackgroundSquare"]) {
NSArray *things = [overrideTheme componentsSeparatedByString:@"/"];
path = [%c(Neon) iconPathForBundleID:things[1] fromTheme:things[0]];
}
else path = [%c(Neon) fullPathForImageNamed:name atPath:[NSString stringWithFormat:@"/Library/Themes/%@/Bundles/com.apple.springboard/", overrideTheme]];
if (path) return [UIImage imageWithContentsOfFile:path];
} else {
for (NSString *theme in themes) {
NSString *path = [%c(Neon) fullPathForImageNamed:name atPath:[NSString stringWithFormat:@"/Library/Themes/%@/Bundles/com.apple.springboard/", theme]];
if (path) return [UIImage imageWithContentsOfFile:path];
}
}
return nil;
}
UIImage *customClockBackground(CGSize size, BOOL masked) {
UIImage *custom = clockImageNamed(@"ClockIconBackgroundSquare");
if (!custom) return nil;
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
if (masked) CGContextClipToMask(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height), [%c(Neon) getMaskImage].CGImage);
if ([%c(NeonRenderService) underlay]) [[%c(NeonRenderService) underlay] drawInRect:CGRectMake(0, 0, size.height, size.width)];
[custom drawInRect:CGRectMake(0, 0, size.width, size.height)];
if ([%c(NeonRenderService) overlayImage]) [[%c(NeonRenderService) overlayImage] drawInRect:CGRectMake(0, 0, size.height, size.width)];
custom = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return custom;
}
UIImage *proportionalResize(UIImage *image, CGSize size) {
if (image.size.height <= size.height) return image;
return [image imageOfSize:CGSizeMake(image.size.width * size.height / image.size.height, size.height)];
}
%group AllVersions
%hook SBClockApplicationIconImageView
- (UIImage *)contentsImage { return customClockBackground(%orig.size, YES) ? : %orig; }
- (UIImage *)squareContentsImage { return customClockBackground(%orig.size, NO) ? : %orig; }
%end
%end
%group iOS7To13
%hook SBClockApplicationIconImageView
- (instancetype)initWithFrame:(CGRect)frame {
self = %orig;
NSMutableDictionary *files = [NSMutableDictionary dictionaryWithDictionary:@{
@"ClockIconSecondHand" : @"seconds",
@"ClockIconMinuteHand" : @"minutes",
@"ClockIconHourHand" : @"hours",
@"ClockIconBlackDot" : @"blackDot",
@"ClockIconRedDot" : @"redDot",
@"ClockIconHourMinuteDot" : @"blackDot",
@"ClockIconSecondDot" : @"redDot"
}];
if (kCFCoreFoundationVersionNumber >= 1665.15) {
[files setObject:@"hourMinuteDot" forKey:@"ClockIconBlackDot"];
[files setObject:@"secondDot" forKey:@"ClockIconRedDot"];
[files setObject:@"hourMinuteDot" forKey:@"ClockIconHourMinuteDot"];
[files setObject:@"secondDot" forKey:@"ClockIconSecondDot"];
}
for (NSString *key in [files allKeys]) {
UIImage *image = clockImageNamed(key);
if (!image) continue;
const char *ivarName = [[@"_" stringByAppendingString:[files objectForKey:key]] cStringUsingEncoding:NSUTF8StringEncoding];
MSHookIvar<CALayer *>(self, ivarName).backgroundColor = [UIColor clearColor].CGColor;
MSHookIvar<CALayer *>(self, ivarName).contents = (id)[image CGImage];
}
return self;
}
%end
%end
%group iOS14Later
%hook SBClockApplicationIconImageView
SBHClockApplicationIconImageMetrics origMetrics;
// override this so that the original instance is forced to be recreated when we initialize our own icon for the render (so that the custom arrows get updated and applied)
+ (SBHClockHandsImageSet *)imageSetForMetrics:(SBHClockApplicationIconImageMetrics *)metrics { return [self makeImageSetForMetrics:metrics]; }
+ (SBHClockHandsImageSet *)makeImageSetForMetrics:(SBHClockApplicationIconImageMetrics *)metrics {
SBHClockHandsImageSet *customSet = %orig;
origMetrics = MSHookIvar<SBHClockApplicationIconImageMetrics>(customSet, "_metrics");
if (UIImage *seconds = clockImageNamed(@"ClockIconSecondHand")) customSet.seconds = proportionalResize(seconds, customSet.seconds.size);
if (UIImage *minutes = clockImageNamed(@"ClockIconMinuteHand")) customSet.minutes = proportionalResize(minutes, customSet.minutes.size);
if (UIImage *hours = clockImageNamed(@"ClockIconHourHand")) customSet.hours = proportionalResize(hours, customSet.hours.size);
if (UIImage *hourMinuteDot = clockImageNamed(@"ClockIconBlackDot")) customSet.hourMinuteDot = proportionalResize(hourMinuteDot, customSet.hourMinuteDot.size);
if (UIImage *secondDot = clockImageNamed(@"ClockIconRedDot")) customSet.secondDot = proportionalResize(secondDot, customSet.secondDot.size);
return customSet;
}
%end
%end
%group Render
%hook SpringBoard
- (void)applicationDidFinishLaunching:(id)application {
%orig;
CPDistributedMessagingCenter *center = [CPDistributedMessagingCenter centerNamed:@"com.artikus.neonboard"];
[center runServerOnCurrentThread];
[center registerForMessageName:@"renderClockIcon" target:self selector:@selector(renderClockIcon)];
}
%new
- (void)renderClockIcon {
// update prefs first
[%c(Neon) loadPrefs];
if ([[[%c(Neon) prefs] objectForKey:@"kNoStaticClock"] boolValue]) return;
overrideTheme = [[%c(Neon) overrideThemes] objectForKey:@"com.apple.mobiletimer"];
if ((overrideTheme && [overrideTheme isEqualToString:@"none"]) || ![%c(Neon) iconPathForBundleID:@"com.apple.mobiletimer"]) return;
if ([%c(Neon) themes] && [%c(Neon) themes].count > 0) themes = [%c(Neon) themes];
// stuff
CGSize size = [%c(Neon) homescreenIconSize];
CGRect rect = CGRectMake(0, 0, size.width, size.height);
SBClockApplicationIconImageView *view = [[%c(SBClockApplicationIconImageView) alloc] initWithFrame:rect];
view.showsSquareCorners = YES;
if (kCFCoreFoundationVersionNumber >= 1740) [view applyMetrics:origMetrics];
[view _setAnimating:NO];
// these can be probably calculated via degrees but i'm lazy; i wish setOverrideDate existed on ios <13 :/
[MSHookIvar<CALayer *>(view, "_hours") setAffineTransform:CGAffineTransformMake(0.577, -0.816, 0.816, 0.577, 0, 0)];
[MSHookIvar<CALayer *>(view, "_minutes") setAffineTransform:CGAffineTransformMake(0.453, 0.891, -0.891, 0.453, 0, 0)];
[MSHookIvar<CALayer *>(view, "_seconds") setAffineTransform:CGAffineTransformMakeRotation(M_PI)];
// render stuff
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
if ([[[%c(Neon) prefs] objectForKey:@"kMaskRendered"] boolValue])
CGContextClipToMask(UIGraphicsGetCurrentContext(), rect, [%c(Neon) getMaskImage].CGImage);
if (kCFCoreFoundationVersionNumber < 1740) {
UIImage *background = [UIImage _applicationIconImageForBundleIdentifier:@"com.apple.mobiletimer" format:2 scale:[UIScreen mainScreen].scale];
[background drawInRect:rect];
}
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
[UIImagePNGRepresentation(finalImage) writeToFile:[[%c(Neon) renderDir] stringByAppendingPathComponent:@"NeonStaticClockIcon.png"] atomically:YES];
UIGraphicsEndImageContext();
}
%end
%end
%ctor {
if (kCFCoreFoundationVersionNumber < 847.20) return; // iOS 7 introduced live clock so
if (!%c(Neon)) dlopen("/Library/MobileSubstrate/DynamicLibraries/NeonEngine.dylib", RTLD_LAZY);
if (!%c(Neon)) return;
%init(Render);
overrideTheme = [[%c(Neon) overrideThemes] objectForKey:@"com.apple.mobiletimer"];
if (overrideTheme && [overrideTheme isEqualToString:@"none"]) return;
if ([%c(Neon) themes] && [%c(Neon) themes].count > 0) {
themes = [%c(Neon) themes];
%init(AllVersions);
if (kCFCoreFoundationVersionNumber >= 1740) %init(iOS14Later);
else %init(iOS7To13);
}
}