]> git.cameronkatri.com Git - tweaks.git/blob - QuickActions/Tweak.x
I lost track of all the changes
[tweaks.git] / QuickActions / Tweak.x
1 /*
2 * Copyright (C) 2021 Cameron Katri
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 #import <Foundation/NSUserDefaults.h>
18 #import <CoreGraphics/CoreGraphics.h>
19 #import <UIKit/UIKit.h>
20
21 #import "Tweak.h"
22
23 void openApplication(NSString *bundleID)
24 {
25 FBSOpenApplicationOptions* opts = [%c(FBSOpenApplicationOptions) optionsWithDictionary:@{
26 @"__LaunchOrigin" : @"BulletinDestinationCoverSheet",
27 @"__PromptUnlockDevice" : @YES,
28 @"__UnlockDevice" : @YES,
29 @"__LaunchImage" : @"",
30 @"__Actions" : @[]
31 }];
32 FBSystemServiceOpenApplicationRequest* request = [%c(FBSystemServiceOpenApplicationRequest) request];
33 request.options = opts;
34 request.bundleIdentifier = bundleID;
35 request.trusted = YES;
36 request.clientProcess = [[%c(FBProcessManager) sharedInstance] systemApplicationProcess];
37
38 [[%c(SBMainWorkspace) sharedInstance] systemService:[%c(FBSystemService) sharedInstance] handleOpenApplicationRequest:request withCompletion:^{}];
39 }
40
41 %hook CSQuickActionsView
42
43 %property (nonatomic, retain) NSMutableArray * leftButtons;
44 %property (nonatomic, retain) NSMutableArray * rightButtons;
45 %property (nonatomic) BOOL leftOpen;
46 %property (nonatomic) BOOL rightOpen;
47 %property (nonatomic) BOOL collapseLeft;
48 %property (nonatomic) BOOL collapseRight;
49 %property (nonatomic,retain) DNDStateService *stateService;
50
51 -(id)initWithFrame:(CGRect)arg1 delegate:(id)arg2
52 {
53 id o = %orig;
54
55 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.cameronkatri.quickactions"];
56
57 NSArray *leftButtons = [defaults arrayForKey:@"leftButtons"];
58 if (leftButtons == nil)
59 leftButtons = @[@"com.apple.flashlight"];
60 NSArray *rightButtons = [defaults arrayForKey:@"rightButtons"];
61 if (rightButtons == nil)
62 rightButtons = @[@"com.apple.camera"];
63
64 self.leftButtons = [[NSMutableArray alloc] init];
65 self.rightButtons = [[NSMutableArray alloc] init];
66
67 self.collapseLeft = [leftButtons count] > 1 ? true : false;
68 self.leftOpen = !self.collapseLeft;
69
70 if ([leftButtons count] == 0) {
71 [self.flashlightButton setHidden:1];
72 } else if ([leftButtons count] == 1) {
73 [self.flashlightButton setBundleID:leftButtons[0]];
74 } else if ([leftButtons count] > 1) {
75 [self.flashlightButton setImage:nil];
76 for (int i = 0; i < [leftButtons count]; i++) {
77 CSQuickActionsButton *button = [[CSQuickActionsButton alloc] initWithType:(i + 2)];
78 [button setBundleID:leftButtons[i]];
79
80 [button setBackgroundEffectViewGroupName:[self _buttonGroupName]];
81 [button setLegibilitySettings:[self legibilitySettings]];
82 [button setPermitted:1];
83
84 [self insertSubview:button belowSubview:self.flashlightButton];
85 [self _addTargetsToButton:button];
86 [self.leftButtons addObject:button];
87 }
88 }
89
90 self.collapseRight = [rightButtons count] > 1 ? true : false;
91 self.rightOpen = !self.collapseRight;
92
93 if ([rightButtons count] == 0) {
94 [self.cameraButton setHidden:1];
95 } else if ([rightButtons count] == 1) {
96 [self.cameraButton setBundleID:rightButtons[0]];
97 } else if ([rightButtons count] > 1) {
98 [self.cameraButton setImage:nil];
99 for (int i = 0; i < [rightButtons count]; i++) {
100 CSQuickActionsButton *button = [[CSQuickActionsButton alloc] initWithType:(i + 2)];
101 [button setBundleID:rightButtons[i]];
102
103 [button setBackgroundEffectViewGroupName:[self _buttonGroupName]];
104 [button setLegibilitySettings:[self legibilitySettings]];
105 [button setPermitted:1];
106
107 [self insertSubview:button belowSubview:self.cameraButton];
108 [self _addTargetsToButton:button];
109 [self.rightButtons addObject:button];
110 }
111 }
112
113 // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDND:) name:@"SBQuietModeStatusChangedNotification" object:nil];
114 // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDND:) name:@"QuickActionsUpdateDND" object:nil];
115 self.stateService = (DNDStateService *)[objc_getClass("DNDStateService") serviceForClientIdentifier:@"com.apple.donotdisturb.control-center.module"];
116 [self.stateService addStateUpdateListener:self withCompletionHandler:nil];
117
118 return o;
119 }
120
121 %new
122 -(CGRect)rightFrameForButton:(CSQuickActionsButton*)button
123 {
124 CGRect cameraFrame = [[self cameraButton] frame];
125 if (self.rightOpen) {
126 return CGRectMake(cameraFrame.origin.x,
127 cameraFrame.origin.y - ((cameraFrame.size.height * 3/4) * (button.type - 1)),
128 cameraFrame.size.width, cameraFrame.size.height);
129 } else {
130 return cameraFrame;
131 }
132 }
133
134 %new
135 -(CGRect)leftFrameForButton:(CSQuickActionsButton*)button
136 {
137 CGRect flashlightFrame = [[self flashlightButton] frame];
138 if (self.leftOpen) {
139 return CGRectMake(flashlightFrame.origin.x,
140 flashlightFrame.origin.y - ((flashlightFrame.size.height * 3/4) * (button.type - 1)),
141 flashlightFrame.size.width, flashlightFrame.size.height);
142 } else {
143 return flashlightFrame;
144 }
145 }
146
147 -(void)setLegibilitySettings:(id)legibilitySettings
148 {
149 %orig;
150 for (CSQuickActionsButton *button in [self leftButtons])
151 [button setLegibilitySettings:legibilitySettings];
152 for (CSQuickActionsButton *button in [self rightButtons])
153 [button setLegibilitySettings:legibilitySettings];
154 }
155
156 -(void)_layoutQuickActionButtons
157 {
158 %orig;
159
160 UIEdgeInsets insets = [self _buttonOutsets];
161 if (SBFEffectiveHomeButtonType() != 2) {
162 CGRect bounds = [[UIScreen mainScreen] _referenceBounds];
163
164 CGFloat buttonWidth = 50 + insets.right + insets.left;
165 CGFloat buttonHeight = 50 + insets.top + insets.bottom;
166
167 [[self flashlightButton] setEdgeInsets:insets];
168
169 self.flashlightButton.frame = CGRectMake(insets.left,
170 bounds.size.height - buttonHeight - insets.bottom,
171 buttonWidth, buttonHeight);
172
173 [[self cameraButton] setEdgeInsets:insets];
174
175 self.cameraButton.frame = CGRectMake(bounds.size.width - insets.left - buttonWidth,
176 bounds.size.height - buttonHeight - insets.bottom,
177 buttonWidth, buttonHeight);
178 }
179
180 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.cameronkatri.quickactions"];
181
182 CGRect frame = self.flashlightButton.frame;
183 frame.origin.x += [defaults floatForKey:@"LeftOffsetX"];
184 frame.origin.y -= [defaults floatForKey:@"LeftOffsetY"];
185 self.flashlightButton.frame = frame;
186
187 frame = self.cameraButton.frame;
188 frame.origin.x -= [defaults floatForKey:@"RightOffsetX"];
189 frame.origin.y -= [defaults floatForKey:@"RightOffsetY"];
190 self.cameraButton.frame = frame;
191
192 UIImageSymbolConfiguration *imageConfig = [UIImageSymbolConfiguration configurationWithTextStyle:UIFontTextStyleTitle1];
193 UIImage *image = [UIImage systemImageNamed:@"ellipsis" withConfiguration:imageConfig];
194 if ([self.leftButtons count] > 1) {
195 [self.flashlightButton setImage:image];
196 ((UIImageView*)[self.flashlightButton valueForKey:@"_contentView"]).contentMode = UIViewContentModeScaleAspectFit;
197 }
198 if ([self.rightButtons count] > 1) {
199 [self.cameraButton setImage:image];
200 ((UIImageView*)[self.cameraButton valueForKey:@"_contentView"]).contentMode = UIViewContentModeScaleAspectFit;
201 }
202
203 for (CSQuickActionsButton *button in [self leftButtons]) {
204 [button setEdgeInsets:insets];
205 button.frame = [self leftFrameForButton:button];
206 [button setHidden:!self.rightOpen];
207 }
208 for (CSQuickActionsButton *button in [self rightButtons]) {
209 [button setEdgeInsets:insets];
210 button.frame = [self rightFrameForButton:button];
211 [button setHidden:!self.rightOpen];
212 }
213
214 [self updateDND:nil];
215 }
216
217 -(void)handleButtonPress:(CSQuickActionsButton *)button
218 {
219 [button setSelected:false];
220
221 if (button.type == 0 && self.collapseRight) {
222 self.rightOpen = !self.rightOpen;
223 [UIView animateWithDuration:0.25
224 delay:0
225 options:UIViewAnimationOptionCurveEaseOut
226 animations:^(void){
227 for (CSQuickActionsButton *button in [self rightButtons]) {
228 button.frame = [self rightFrameForButton:button];
229 if (self.rightOpen)
230 [button setHidden:0];
231 }
232 }
233 completion:^(BOOL finished) {
234 for (CSQuickActionsButton *button in [self rightButtons])
235 [button setHidden:!self.rightOpen];
236 }];
237 } else if (button.type == 1 && self.collapseLeft) {
238 self.leftOpen = !self.leftOpen;
239 [UIView animateWithDuration:0.25
240 delay:0
241 options:UIViewAnimationOptionCurveEaseOut
242 animations:^(void) {
243 for (CSQuickActionsButton *button in [self leftButtons]) {
244 button.frame = [self leftFrameForButton:button];
245 if (self.leftOpen)
246 [button setHidden:0];
247 }
248 }
249 completion:^(BOOL finished) {
250 for (CSQuickActionsButton *button in [self leftButtons])
251 [button setHidden:!self.leftOpen];
252 }];
253 } else if ([button.bundleID isEqualToString:@"com.apple.flashlight"]) {
254 [self.delegate _toggleFlashlight];
255 } else if ([button.bundleID isEqualToString:@"com.apple.camera"]) {
256 [self.delegate _launchCamera];
257 } else if ([button.bundleID isEqualToString:@"com.apple.donotdisturb"]) {
258 [self setDoNotDisturb:!self.isDNDActive];
259 } else if (button.bundleID)
260 openApplication(button.bundleID);
261 else {
262 %orig;
263 return;
264 }
265
266 /* This will make the CC module be correct */
267 [self.delegate _resetIdleTimer];
268 [self.delegate sendAction:[CSAction actionWithType:5]];
269
270 return;
271 }
272
273 -(void)handleButtonTouchBegan:(CSQuickActionsButton *)button
274 {
275 if (button.bundleID != nil ||
276 (button.type == 0 && self.collapseRight) ||
277 (button.type == 1 && self.collapseLeft))
278 return;
279 %orig;
280 }
281
282 -(void)handleButtonTouchEnded:(CSQuickActionsButton *)button
283 {
284 if (button.bundleID != nil ||
285 (button.type == 0 && self.collapseRight) ||
286 (button.type == 1 && self.collapseLeft))
287 return;
288 %orig;
289 }
290
291 -(void)setFlashlightOn:(BOOL)arg
292 {
293 if ([self.flashlightButton.bundleID isEqualToString:@"com.apple.flashlight"])
294 [self.flashlightButton setSelected:arg];
295 if ([self.cameraButton.bundleID isEqualToString:@"com.apple.flashlight"])
296 [self.cameraButton setSelected:arg];
297 for (CSQuickActionsButton *button in [self leftButtons])
298 if ([button.bundleID isEqualToString:@"com.apple.flashlight"])
299 [button setSelected:arg];
300 for (CSQuickActionsButton *button in [self rightButtons])
301 if ([button.bundleID isEqualToString:@"com.apple.flashlight"])
302 [button setSelected:arg];
303 }
304
305 %new
306 -(void)setDoNotDisturb:(BOOL)state
307 {
308 DNDModeAssertionService *assertionService = (DNDModeAssertionService *)[objc_getClass("DNDModeAssertionService") serviceForClientIdentifier:@"com.apple.donotdisturb.control-center.module"];
309
310 if (state) {
311 DNDModeAssertionDetails *newAssertion = [objc_getClass("DNDModeAssertionDetails") userRequestedAssertionDetailsWithIdentifier:@"com.apple.control-center.manual-toggle" modeIdentifier:@"com.apple.donotdisturb.mode.default" lifetime:nil];
312 [assertionService takeModeAssertionWithDetails:newAssertion error:NULL];
313 } else {
314 [assertionService invalidateAllActiveModeAssertionsWithError:NULL];
315 }
316
317 [[NSNotificationCenter defaultCenter] postNotificationName:@"SBQuietModeStatusChangedNotification" object:nil];
318 }
319
320 %new
321 -(BOOL)isDNDActive
322 {
323
324 // DNDStateService *stateService = (DNDStateService *)[objc_getClass("DNDStateService") serviceForClientIdentifier:@"com.apple.donotdisturb.control-center.module"];
325 return [[self.stateService queryCurrentStateWithError:nil] isActive];
326 }
327
328 %new
329 -(void)updateDND:(NSNotification *)notif
330 {
331 BOOL active = [self isDNDActive];
332
333 if ([self.flashlightButton.bundleID isEqualToString:@"com.apple.donotdisturb"])
334 [self.flashlightButton setSelected:active];
335 if ([self.cameraButton.bundleID isEqualToString:@"com.apple.donotdisturb"])
336 [self.cameraButton setSelected:active];
337 for (CSQuickActionsButton *button in [self leftButtons])
338 if ([button.bundleID isEqualToString:@"com.apple.donotdisturb"])
339 [button setSelected:active];
340 for (CSQuickActionsButton *button in [self rightButtons])
341 if ([button.bundleID isEqualToString:@"com.apple.donotdisturb"])
342 [button setSelected:active];
343 }
344
345 %new
346 -(void)stateService:(id)arg1 didReceiveDoNotDisturbStateUpdate:(id)arg2
347 {
348 dispatch_async(dispatch_get_main_queue(), ^{
349 [self updateDND:nil];
350 });
351 }
352
353 %end
354
355 %hook CSQuickActionsButton
356
357 %property (nonatomic, retain) NSString *bundleID;
358
359 -(void)setImage:(UIImage *)img
360 {
361 %orig;
362 [[self valueForKey:@"_contentView"] setImage:img];
363 }
364
365 -(void)setBundleID:(NSString*)bundleID
366 {
367 %orig;
368 if ([bundleID isEqualToString:@"com.apple.camera"]) {
369 [self setImage:[self _imageWithName:@"OrbCamera"]];
370 [self setLatching:FALSE];
371 } else if ([bundleID isEqualToString:@"com.apple.flashlight"]) {
372 [self setImage:[self _imageWithName:@"OrbFlashlightOff"]];
373 [self setSelectedImage:[self _imageWithName:@"OrbFlashlightOff"]];
374 [self setLatching:TRUE];
375 } else if (([bundleID isEqualToString:@"com.apple.donotdisturb"])) {
376 UIImageSymbolConfiguration *imageConfig = [UIImageSymbolConfiguration configurationWithTextStyle:UIFontTextStyleTitle2];
377 [self setImage:[UIImage systemImageNamed:@"moon.fill" withConfiguration:imageConfig]];
378 [self setSelectedImage:[UIImage systemImageNamed:@"moon.fill" withConfiguration:imageConfig]];
379 ((UIImageView *)[self valueForKey:@"_contentView"]).contentMode = UIViewContentModeScaleAspectFit;
380 [self setLatching:TRUE];
381 } else {
382 [self setImage:[UIImage _applicationIconImageForBundleIdentifier:bundleID format:0 scale:[UIScreen mainScreen].scale]];
383 [self setLatching:FALSE];
384 }
385 }
386
387 -(void)setPermitted:(BOOL)permitted
388 {
389 %orig(YES);
390 }
391
392 %end
393
394 %hook CSQuickActionsViewController
395
396 -(BOOL)hasCamera
397 {
398 return true;
399 }
400
401 -(BOOL)hasFlashlight
402 {
403 return true;
404 }
405
406 +(BOOL)deviceSupportsButtons
407 {
408 return true;
409 }
410
411 %end
412
413 // %hook DNDNotificationsService
414
415 // -(void)stateService:(id)arg1 didReceiveDoNotDisturbStateUpdate:(id)arg2{
416 // %orig;
417
418 // [[NSNotificationCenter defaultCenter] postNotificationName:@"QuickActionsUpdateDND" object:nil];
419 // }
420
421 // %end
422
423 // vim: filetype=logos