2 * Copyright (C) 2021 Cameron Katri
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.
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.
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/>.
17 #import <Foundation/NSUserDefaults.h>
18 #import <CoreGraphics/CoreGraphics.h>
19 #import <UIKit/UIKit.h>
23 void openApplication(NSString *bundleID)
25 FBSOpenApplicationOptions* opts = [%c(FBSOpenApplicationOptions) optionsWithDictionary:@{
26 @"__LaunchOrigin" : @"BulletinDestinationCoverSheet",
27 @"__PromptUnlockDevice" : @YES,
28 @"__UnlockDevice" : @YES,
29 @"__LaunchImage" : @"",
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];
38 [[%c(SBMainWorkspace) sharedInstance] systemService:[%c(FBSystemService) sharedInstance] handleOpenApplicationRequest:request withCompletion:^{}];
41 %hook CSQuickActionsView
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;
51 -(id)initWithFrame:(CGRect)arg1 delegate:(id)arg2
55 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.cameronkatri.quickactions"];
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"];
64 self.leftButtons = [[NSMutableArray alloc] init];
65 self.rightButtons = [[NSMutableArray alloc] init];
67 self.collapseLeft = [leftButtons count] > 1 ? true : false;
68 self.leftOpen = !self.collapseLeft;
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]];
80 [button setBackgroundEffectViewGroupName:[self _buttonGroupName]];
81 [button setLegibilitySettings:[self legibilitySettings]];
82 [button setPermitted:1];
84 [self insertSubview:button belowSubview:self.flashlightButton];
85 [self _addTargetsToButton:button];
86 [self.leftButtons addObject:button];
90 self.collapseRight = [rightButtons count] > 1 ? true : false;
91 self.rightOpen = !self.collapseRight;
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]];
103 [button setBackgroundEffectViewGroupName:[self _buttonGroupName]];
104 [button setLegibilitySettings:[self legibilitySettings]];
105 [button setPermitted:1];
107 [self insertSubview:button belowSubview:self.cameraButton];
108 [self _addTargetsToButton:button];
109 [self.rightButtons addObject:button];
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];
122 -(CGRect)rightFrameForButton:(CSQuickActionsButton*)button
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);
135 -(CGRect)leftFrameForButton:(CSQuickActionsButton*)button
137 CGRect flashlightFrame = [[self flashlightButton] frame];
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);
143 return flashlightFrame;
147 -(void)setLegibilitySettings:(id)legibilitySettings
150 for (CSQuickActionsButton *button in [self leftButtons])
151 [button setLegibilitySettings:legibilitySettings];
152 for (CSQuickActionsButton *button in [self rightButtons])
153 [button setLegibilitySettings:legibilitySettings];
156 -(void)_layoutQuickActionButtons
160 UIEdgeInsets insets = [self _buttonOutsets];
161 if (SBFEffectiveHomeButtonType() != 2) {
162 CGRect bounds = [[UIScreen mainScreen] _referenceBounds];
164 CGFloat buttonWidth = 50 + insets.right + insets.left;
165 CGFloat buttonHeight = 50 + insets.top + insets.bottom;
167 [[self flashlightButton] setEdgeInsets:insets];
169 self.flashlightButton.frame = CGRectMake(insets.left,
170 bounds.size.height - buttonHeight - insets.bottom,
171 buttonWidth, buttonHeight);
173 [[self cameraButton] setEdgeInsets:insets];
175 self.cameraButton.frame = CGRectMake(bounds.size.width - insets.left - buttonWidth,
176 bounds.size.height - buttonHeight - insets.bottom,
177 buttonWidth, buttonHeight);
180 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.cameronkatri.quickactions"];
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;
187 frame = self.cameraButton.frame;
188 frame.origin.x -= [defaults floatForKey:@"RightOffsetX"];
189 frame.origin.y -= [defaults floatForKey:@"RightOffsetY"];
190 self.cameraButton.frame = frame;
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;
198 if ([self.rightButtons count] > 1) {
199 [self.cameraButton setImage:image];
200 ((UIImageView*)[self.cameraButton valueForKey:@"_contentView"]).contentMode = UIViewContentModeScaleAspectFit;
203 for (CSQuickActionsButton *button in [self leftButtons]) {
204 [button setEdgeInsets:insets];
205 button.frame = [self leftFrameForButton:button];
206 [button setHidden:!self.rightOpen];
208 for (CSQuickActionsButton *button in [self rightButtons]) {
209 [button setEdgeInsets:insets];
210 button.frame = [self rightFrameForButton:button];
211 [button setHidden:!self.rightOpen];
214 [self updateDND:nil];
217 -(void)handleButtonPress:(CSQuickActionsButton *)button
219 [button setSelected:false];
221 if (button.type == 0 && self.collapseRight) {
222 self.rightOpen = !self.rightOpen;
223 [UIView animateWithDuration:0.25
225 options:UIViewAnimationOptionCurveEaseOut
227 for (CSQuickActionsButton *button in [self rightButtons]) {
228 button.frame = [self rightFrameForButton:button];
230 [button setHidden:0];
233 completion:^(BOOL finished) {
234 for (CSQuickActionsButton *button in [self rightButtons])
235 [button setHidden:!self.rightOpen];
237 } else if (button.type == 1 && self.collapseLeft) {
238 self.leftOpen = !self.leftOpen;
239 [UIView animateWithDuration:0.25
241 options:UIViewAnimationOptionCurveEaseOut
243 for (CSQuickActionsButton *button in [self leftButtons]) {
244 button.frame = [self leftFrameForButton:button];
246 [button setHidden:0];
249 completion:^(BOOL finished) {
250 for (CSQuickActionsButton *button in [self leftButtons])
251 [button setHidden:!self.leftOpen];
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);
266 /* This will make the CC module be correct */
267 [self.delegate _resetIdleTimer];
268 [self.delegate sendAction:[CSAction actionWithType:5]];
273 -(void)handleButtonTouchBegan:(CSQuickActionsButton *)button
275 if (button.bundleID != nil ||
276 (button.type == 0 && self.collapseRight) ||
277 (button.type == 1 && self.collapseLeft))
282 -(void)handleButtonTouchEnded:(CSQuickActionsButton *)button
284 if (button.bundleID != nil ||
285 (button.type == 0 && self.collapseRight) ||
286 (button.type == 1 && self.collapseLeft))
291 -(void)setFlashlightOn:(BOOL)arg
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];
306 -(void)setDoNotDisturb:(BOOL)state
308 DNDModeAssertionService *assertionService = (DNDModeAssertionService *)[objc_getClass("DNDModeAssertionService") serviceForClientIdentifier:@"com.apple.donotdisturb.control-center.module"];
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];
314 [assertionService invalidateAllActiveModeAssertionsWithError:NULL];
317 [[NSNotificationCenter defaultCenter] postNotificationName:@"SBQuietModeStatusChangedNotification" object:nil];
324 // DNDStateService *stateService = (DNDStateService *)[objc_getClass("DNDStateService") serviceForClientIdentifier:@"com.apple.donotdisturb.control-center.module"];
325 return [[self.stateService queryCurrentStateWithError:nil] isActive];
329 -(void)updateDND:(NSNotification *)notif
331 BOOL active = [self isDNDActive];
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];
346 -(void)stateService:(id)arg1 didReceiveDoNotDisturbStateUpdate:(id)arg2
348 dispatch_async(dispatch_get_main_queue(), ^{
349 [self updateDND:nil];
355 %hook CSQuickActionsButton
357 %property (nonatomic, retain) NSString *bundleID;
359 -(void)setImage:(UIImage *)img
362 [[self valueForKey:@"_contentView"] setImage:img];
365 -(void)setBundleID:(NSString*)bundleID
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];
382 [self setImage:[UIImage _applicationIconImageForBundleIdentifier:bundleID format:0 scale:[UIScreen mainScreen].scale]];
383 [self setLatching:FALSE];
387 -(void)setPermitted:(BOOL)permitted
394 %hook CSQuickActionsViewController
406 +(BOOL)deviceSupportsButtons
413 // %hook DNDNotificationsService
415 // -(void)stateService:(id)arg1 didReceiveDoNotDisturbStateUpdate:(id)arg2{
418 // [[NSNotificationCenter defaultCenter] postNotificationName:@"QuickActionsUpdateDND" object:nil];
423 // vim: filetype=logos