]> git.cameronkatri.com Git - tweaks.git/blob - QuickActions/Tweak.x
QuickActions: Massive improvements
[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
50 -(id)initWithFrame:(CGRect)arg1 delegate:(id)arg2
51 {
52 id o = %orig;
53
54 NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.cameronkatri.quickactions"];
55
56 NSArray *leftButtons = (NSArray*)[defaults objectForKey:@"leftButtons"];
57 NSArray *rightButtons = (NSArray*)[defaults objectForKey:@"rightButtons"];
58
59
60 self.leftButtons = [[NSMutableArray alloc] init];
61 self.rightButtons = [[NSMutableArray alloc] init];
62
63 self.collapseLeft = [leftButtons count] > 1 ? true : false;
64 self.leftOpen = !self.collapseLeft;
65
66 if ([leftButtons count] == 1) {
67 [self.flashlightButton setBundleID:leftButtons[0]];
68 } else if ([leftButtons count] > 1) {
69 [self.flashlightButton setImage:nil];
70 for (int i = 0; i < [leftButtons count]; i++) {
71 CSQuickActionsButton *button = [[CSQuickActionsButton alloc] initWithType:(i + 2)];
72 [button setBundleID:leftButtons[i]];
73
74 [button setBackgroundEffectViewGroupName:[self _buttonGroupName]];
75 [button setLegibilitySettings:[self legibilitySettings]];
76 [button setPermitted:1];
77
78 [self insertSubview:button belowSubview:self.flashlightButton];
79 [self _addTargetsToButton:button];
80 [self.leftButtons addObject:button];
81 }
82 }
83
84 self.collapseRight = [rightButtons count] > 1 ? true : false;
85 self.rightOpen = !self.collapseRight;
86
87 if ([rightButtons count] == 1) {
88 [self.cameraButton setBundleID:rightButtons[0]];
89 } else if ([rightButtons count] > 1) {
90 [self.cameraButton setImage:nil];
91 for (int i = 0; i < [rightButtons count]; i++) {
92 CSQuickActionsButton *button = [[CSQuickActionsButton alloc] initWithType:(i + 2)];
93 [button setBundleID:rightButtons[i]];
94
95 [button setBackgroundEffectViewGroupName:[self _buttonGroupName]];
96 [button setLegibilitySettings:[self legibilitySettings]];
97 [button setPermitted:1];
98
99 [self insertSubview:button belowSubview:self.cameraButton];
100 [self _addTargetsToButton:button];
101 [self.rightButtons addObject:button];
102 }
103 }
104
105 return o;
106 }
107
108 %new
109 -(CGRect)rightFrameForButton:(CSQuickActionsButton*)button
110 {
111 CGRect cameraFrame = [[self cameraButton] frame];
112 if (self.rightOpen) {
113 return CGRectMake(cameraFrame.origin.x,
114 cameraFrame.origin.y - ((cameraFrame.size.height * 3/4) * ((button.type + 1) / 2)),
115 cameraFrame.size.width, cameraFrame.size.height);
116 } else {
117 return cameraFrame;
118 }
119 }
120
121 %new
122 -(CGRect)leftFrameForButton:(CSQuickActionsButton*)button
123 {
124 CGRect flashlightFrame = [[self flashlightButton] frame];
125 if (self.leftOpen) {
126 return CGRectMake(flashlightFrame.origin.x,
127 flashlightFrame.origin.y - ((flashlightFrame.size.height * 3/4) * (button.type - 1)),
128 flashlightFrame.size.width, flashlightFrame.size.height);
129 } else {
130 return flashlightFrame;
131 }
132 }
133
134 -(void)setLegibilitySettings:(id)legibilitySettings
135 {
136 %orig;
137 for (CSQuickActionsButton *button in [self leftButtons])
138 [button setLegibilitySettings:legibilitySettings];
139 for (CSQuickActionsButton *button in [self rightButtons])
140 [button setLegibilitySettings:legibilitySettings];
141 }
142
143 -(void)_layoutQuickActionButtons
144 {
145 %orig;
146
147 UIEdgeInsets insets = [self _buttonOutsets];
148 if (SBFEffectiveHomeButtonType() != 2) {
149 CGRect bounds = [[UIScreen mainScreen] _referenceBounds];
150
151 CGFloat buttonWidth = 50 + insets.right + insets.left;
152 CGFloat buttonHeight = 50 + insets.top + insets.bottom;
153
154 [[self flashlightButton] setEdgeInsets:insets];
155
156 self.flashlightButton.frame = CGRectMake(insets.left,
157 bounds.size.height - buttonHeight - insets.bottom,
158 buttonWidth, buttonHeight);
159
160 [[self cameraButton] setEdgeInsets:insets];
161
162 self.cameraButton.frame = CGRectMake(bounds.size.width - insets.left - buttonWidth,
163 bounds.size.height - buttonHeight - insets.bottom,
164 buttonWidth, buttonHeight);
165
166 }
167
168 for (CSQuickActionsButton *button in [self leftButtons]) {
169 [button setEdgeInsets:insets];
170 button.frame = [self leftFrameForButton:button];
171 }
172 for (CSQuickActionsButton *button in [self rightButtons]) {
173 [button setEdgeInsets:insets];
174 button.frame = [self rightFrameForButton:button];
175 }
176 }
177
178 -(void)handleButtonPress:(CSQuickActionsButton *)button
179 {
180 [button setSelected:false];
181
182 if (button.type == 0 && self.collapseRight) {
183 [UIView animateWithDuration:0.25
184 delay:0
185 options:UIViewAnimationOptionCurveEaseOut
186 animations:^(void){
187 for (CSQuickActionsButton *button in [self rightButtons]) {
188 button.frame = [self rightFrameForButton:button];
189 }
190 }
191 completion:NULL];
192 self.rightOpen = !self.rightOpen;
193 } else if (button.type == 1 && self.collapseLeft) {
194 [UIView animateWithDuration:0.25
195 delay:0
196 options:UIViewAnimationOptionCurveEaseOut
197 animations:^(void){
198 for (CSQuickActionsButton *button in [self leftButtons]) {
199 button.frame = [self leftFrameForButton:button];
200 }
201 }
202 completion:NULL];
203 self.leftOpen = !self.leftOpen;
204 } else if (button.bundleID) {
205 openApplication(button.bundleID);
206 } else
207 %orig;
208 return;
209 }
210
211 -(void)handleButtonTouchBegan:(CSQuickActionsButton *)button
212 {
213 if (button.bundleID != nil ||
214 (button.type == 0 && self.collapseRight) ||
215 (button.type == 1 && self.collapseLeft))
216 return;
217 %orig;
218 }
219
220 -(void)handleButtonTouchEnded:(CSQuickActionsButton *)button
221 {
222 if (button.bundleID != nil ||
223 (button.type == 0 && self.collapseRight) ||
224 (button.type == 1 && self.collapseLeft))
225 return;
226 %orig;
227 }
228
229 %end
230
231 %hook CSQuickActionsButton
232
233 %property (nonatomic, retain) NSString *bundleID;
234
235 -(void)setImage:(UIImage *)img
236 {
237 %orig;
238 [[self valueForKey:@"_contentView"] setImage:img];
239 }
240
241 -(void)setBundleID:(NSString*)bundleID
242 {
243 %orig;
244 [self setImage:[UIImage _applicationIconImageForBundleIdentifier:bundleID format:0 scale:[UIScreen mainScreen].scale]];
245 }
246
247 %end
248
249 %hook CSQuickActionsViewController
250
251 -(BOOL)hasCamera
252 {
253 return true;
254 }
255
256 -(BOOL)hasFlashlight
257 {
258 return true;
259 }
260
261 +(BOOL)deviceSupportsButtons
262 {
263 return true;
264 }
265
266 %end
267
268 // vim: filetype=logos