summaryrefslogblamecommitdiffstats
path: root/QuickActions/Tweak.x
blob: 7e9a505f8c0a78b56d6eee0a837f78f70423566a (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16















                                                                              
                                     

                                     


                 



















                                                                                                                                                          





                                                            
                                                            






                                                                                                               





                                                                       
 





                                                                   


                                                    



















                                                                                                           


                                                
















                                                                                                           


                                                                                                                                                             


                 



                                    
















                                                                                                                 

 
    
                                                                          

                                                         
                   
                                                       
                                                                                                     






                                                                         
                                                                         

                                                                 
                   
























                                                                                                             




                                                                             













                                                                                                   

         











                                                                                                               








                                                                                                                                  



                                                                  

                                                                        


                                                                   

                                                                         
         

                             

 


                                                       

                                                     
                                                 



                                                                                                                                         
                                                                                                                                                                                           
                                                                                                                                                                                                             


                                                                                                                                                                     
                                                                                  



                                                                                                                                                                                                 
                                                           
                                               


                                                                                                                                         

                                                                                                                                                                                          
                                                                                                                                                                                                           


                                                                                                                                                                     
                                                                                  










                                                                                                                                                                                                
                                                 
              
                      


                       
                                                             


                                                               




                                                            


                                                                   
                       
              



                                                            


                                                                   
                       
              

 





























































                                                                                                                                                                                                                                                            



                          
                                                 






                                                          
                                      
 
              





















                                                                                                                                        



    


                                  
 
                    

 
                    
 
                    

 






                            
                      
/*
 * Copyright (C) 2021 Cameron Katri
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
#import <Foundation/NSUserDefaults.h>
#import <CoreGraphics/CoreGraphics.h>
#import <UIKit/UIKit.h>

#import "Tweak.h"

void openApplication(NSString *bundleID)
{
	FBSOpenApplicationOptions* opts = [%c(FBSOpenApplicationOptions) optionsWithDictionary:@{
		@"__LaunchOrigin" : @"BulletinDestinationCoverSheet",
		@"__PromptUnlockDevice" : @YES,
		@"__UnlockDevice" : @YES,
		@"__LaunchImage" : @"",
		@"__Actions" : @[]
	}];
	FBSystemServiceOpenApplicationRequest* request = [%c(FBSystemServiceOpenApplicationRequest) request];
	request.options = opts;
	request.bundleIdentifier = bundleID;
	request.trusted = YES;
	request.clientProcess = [[%c(FBProcessManager) sharedInstance] systemApplicationProcess];

	[[%c(SBMainWorkspace) sharedInstance] systemService:[%c(FBSystemService) sharedInstance] handleOpenApplicationRequest:request withCompletion:^{}];
}

%hook CSQuickActionsView

%property (nonatomic, retain) NSMutableArray * leftButtons;
%property (nonatomic, retain) NSMutableArray * rightButtons;
%property (nonatomic) BOOL leftOpen;
%property (nonatomic) BOOL rightOpen;
%property (nonatomic) BOOL collapseLeft;
%property (nonatomic) BOOL collapseRight;
%property (nonatomic, retain) DNDStateService *stateService;

-(id)initWithFrame:(CGRect)arg1 delegate:(id)arg2
{
	id o = %orig;

	NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.cameronkatri.quickactions"];

	NSArray *leftButtons = [defaults arrayForKey:@"leftButtons"];
	if (leftButtons == nil)
		leftButtons = @[@"com.apple.flashlight"];
	NSArray *rightButtons = [defaults arrayForKey:@"rightButtons"];
	if (rightButtons == nil)
		rightButtons = @[@"com.apple.camera"];

	self.leftButtons = [[NSMutableArray alloc] init];
	self.rightButtons = [[NSMutableArray alloc] init];

	self.collapseLeft = [leftButtons count] > 1 ? true : false;
	self.leftOpen = !self.collapseLeft;

	if ([leftButtons count] == 0) {
		[self.flashlightButton setHidden:1];
	} else if ([leftButtons count] == 1) {
		[self.flashlightButton setBundleID:leftButtons[0]];
	} else if ([leftButtons count] > 1) {
		[self.flashlightButton setImage:nil];
		for (int i = 0; i < [leftButtons count]; i++) {
			CSQuickActionsButton *button = [[CSQuickActionsButton alloc] initWithType:(i + 2)];
			[button setBundleID:leftButtons[i]];

			[button setBackgroundEffectViewGroupName:[self _buttonGroupName]];
			[button setLegibilitySettings:[self legibilitySettings]];
			[button setPermitted:1];

			[self insertSubview:button belowSubview:self.flashlightButton];
			[self _addTargetsToButton:button];
			[self.leftButtons addObject:button];
		}
	}

	self.collapseRight = [rightButtons count] > 1 ? true : false;
	self.rightOpen = !self.collapseRight;

	if ([rightButtons count] == 0) {
		[self.cameraButton setHidden:1];
	} else if ([rightButtons count] == 1) {
		[self.cameraButton setBundleID:rightButtons[0]];
	} else if ([rightButtons count] > 1) {
		[self.cameraButton setImage:nil];
		for (int i = 0; i < [rightButtons count]; i++) {
			CSQuickActionsButton *button = [[CSQuickActionsButton alloc] initWithType:(i + 2)];
			[button setBundleID:rightButtons[i]];

			[button setBackgroundEffectViewGroupName:[self _buttonGroupName]];
			[button setLegibilitySettings:[self legibilitySettings]];
			[button setPermitted:1];

			[self insertSubview:button belowSubview:self.cameraButton];
			[self _addTargetsToButton:button];
			[self.rightButtons addObject:button];
		}
	}

	self.stateService = (DNDStateService *)[objc_getClass("DNDStateService") serviceForClientIdentifier:@"com.apple.donotdisturb.control-center.module"];
	[self.stateService addStateUpdateListener:self withCompletionHandler:nil];

	return o;
}

-(void)refreshFlashlightAvailability
{
	%orig;

	[UIView animateWithDuration:0.25
						  delay:0
						options:UIViewAnimationOptionCurveEaseOut
					 animations:^(void){
					 	for (CSQuickActionsButton *button in [self leftButtons])
					 		button.frame = [self leftFrameForButton:button open:NO];
						self.leftOpen = NO;
					 	for (CSQuickActionsButton *button in [self rightButtons])
					 		button.frame = [self rightFrameForButton:button open:NO];
						self.rightOpen = NO;
					 }
					 completion:^(BOOL finished) {
						for (CSQuickActionsButton *button in [self leftButtons])
							[button setHidden:YES];
						for (CSQuickActionsButton *button in [self rightButtons])
							[button setHidden:YES];
                     }];
}

%new
-(CGRect)rightFrameForButton:(CSQuickActionsButton*)button open:(BOOL)open
{
	CGRect cameraFrame = [[self cameraButton] frame];
	if (open) {
		return CGRectMake(cameraFrame.origin.x,
			cameraFrame.origin.y - ((cameraFrame.size.height * 3/4) * (button.type - 1)),
			cameraFrame.size.width, cameraFrame.size.height);
	} else {
		return cameraFrame;
	}
}

%new
-(CGRect)leftFrameForButton:(CSQuickActionsButton*)button open:(BOOL)open
{
	CGRect flashlightFrame = [[self flashlightButton] frame];
	if (open) {
		return CGRectMake(flashlightFrame.origin.x,
			flashlightFrame.origin.y - ((flashlightFrame.size.height * 3/4) * (button.type - 1)),
			flashlightFrame.size.width, flashlightFrame.size.height);
	} else {
		return flashlightFrame;
	}
}

-(void)setLegibilitySettings:(id)legibilitySettings
{
	%orig;
	for (CSQuickActionsButton *button in [self leftButtons])
		[button setLegibilitySettings:legibilitySettings];
	for (CSQuickActionsButton *button in [self rightButtons])
		[button setLegibilitySettings:legibilitySettings];
}

-(void)_layoutQuickActionButtons
{
	%orig;

	UIEdgeInsets insets = [self _buttonOutsets];
	if (SBFEffectiveHomeButtonType() != 2) {
		CGRect bounds = [[UIScreen mainScreen] _referenceBounds];

		// Detect if we are on an iPhone SE and adjust the insets
		// accordingly to not overlap with the text
		if ([[UIScreen mainScreen] nativeBounds].size.height == 1136)
			insets.bottom += 10;

		CGFloat buttonWidth = 50 + insets.right + insets.left;
		CGFloat buttonHeight = 50 + insets.top + insets.bottom;

		[[self flashlightButton] setEdgeInsets:insets];

		self.flashlightButton.frame = CGRectMake(insets.left,
				bounds.size.height - buttonHeight - insets.bottom,
				buttonWidth, buttonHeight);

		[[self cameraButton] setEdgeInsets:insets];

		self.cameraButton.frame = CGRectMake(bounds.size.width - insets.left - buttonWidth,
				bounds.size.height - buttonHeight - insets.bottom,
				buttonWidth, buttonHeight);
	}

	NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.cameronkatri.quickactions"];

	CGRect frame = self.flashlightButton.frame;
	frame.origin.x += [defaults floatForKey:@"LeftOffsetX"];
	frame.origin.y -= [defaults floatForKey:@"LeftOffsetY"];
	self.flashlightButton.frame = frame;

	frame = self.cameraButton.frame;
	frame.origin.x -= [defaults floatForKey:@"RightOffsetX"];
	frame.origin.y -= [defaults floatForKey:@"RightOffsetY"];
	self.cameraButton.frame = frame;

	UIImageSymbolConfiguration *imageConfig = [UIImageSymbolConfiguration configurationWithTextStyle:UIFontTextStyleTitle1];
	UIImage *image = [UIImage systemImageNamed:@"ellipsis" withConfiguration:imageConfig];
	if ([self.leftButtons count] > 1) {
		[self.flashlightButton setImage:image];
		((UIImageView*)[self.flashlightButton valueForKey:@"_contentView"]).contentMode = UIViewContentModeScaleAspectFit;
	}
	if ([self.rightButtons count] > 1) {
		[self.cameraButton setImage:image];
		((UIImageView*)[self.cameraButton valueForKey:@"_contentView"]).contentMode = UIViewContentModeScaleAspectFit;
	}

	for (CSQuickActionsButton *button in [self leftButtons]) {
		[button setEdgeInsets:insets];
		button.frame = [self leftFrameForButton:button open:NO];
		[button setHidden:YES];
	}
	for (CSQuickActionsButton *button in [self rightButtons]) {
		[button setEdgeInsets:insets];
		button.frame = [self rightFrameForButton:button open:NO];
		[button setHidden:YES];
	}

	[self updateDND:nil];
}

-(void)handleButtonPress:(CSQuickActionsButton *)button
{
	[button setSelected:false];

	if (button.type == 0 && self.collapseRight) {
		self.rightOpen = !self.rightOpen;
		[UIView animateWithDuration:0.25
													delay:0
												options:UIViewAnimationOptionCurveEaseOut
										 animations:^(void){
																for (CSQuickActionsButton *button in [self rightButtons]) {
																	button.frame = [self rightFrameForButton:button open:self.rightOpen];
																	if (self.rightOpen)
																		[button setHidden:0];
																}
										 }
										 completion:^(BOOL finished) {
																	for (CSQuickActionsButton *button in [self rightButtons])
																		[button setHidden:!self.rightOpen];
																}];
	} else if (button.type == 1 && self.collapseLeft) {
		self.leftOpen = !self.leftOpen;
		[UIView animateWithDuration:0.25
													delay:0
												options:UIViewAnimationOptionCurveEaseOut
										 animations:^(void) {
																for (CSQuickActionsButton *button in [self leftButtons]) {
																	button.frame = [self leftFrameForButton:button open:self.leftOpen];
																	if (self.leftOpen)
																		[button setHidden:0];
																}
										 }
										 completion:^(BOOL finished) {
																	for (CSQuickActionsButton *button in [self leftButtons])
																		[button setHidden:!self.leftOpen];
																}];
	} else if ([button.bundleID isEqualToString:@"com.apple.flashlight"]) {
		[self.delegate _toggleFlashlight];
	} else if ([button.bundleID isEqualToString:@"com.apple.camera"]) {
		[self.delegate _launchCamera];
	} else if ([button.bundleID isEqualToString:@"com.apple.donotdisturb"]) {
		[self setDoNotDisturb:!self.isDNDActive];
	} else if (button.bundleID)
		openApplication(button.bundleID);
	else {
		%orig;
		return;
	}

	/* This will make the flashlight module be correct */
	[self.delegate _resetIdleTimer];
	[self.delegate sendAction:[CSAction actionWithType:5]];

	return;
}

-(void)handleButtonTouchBegan:(CSQuickActionsButton *)button
{
	if (button.bundleID != nil ||
			(button.type == 0 && self.collapseRight) ||
			(button.type == 1 && self.collapseLeft))
		return;
	%orig;
}

-(void)handleButtonTouchEnded:(CSQuickActionsButton *)button
{
	if (button.bundleID != nil ||
			(button.type == 0 && self.collapseRight) ||
			(button.type == 1 && self.collapseLeft))
		return;
	%orig;
}

-(void)setFlashlightOn:(BOOL)arg
{
	if ([self.flashlightButton.bundleID isEqualToString:@"com.apple.flashlight"])
		[self.flashlightButton setSelected:arg];
	if ([self.cameraButton.bundleID isEqualToString:@"com.apple.flashlight"])
		[self.cameraButton setSelected:arg];
	for (CSQuickActionsButton *button in [self leftButtons])
		if ([button.bundleID isEqualToString:@"com.apple.flashlight"])
			[button setSelected:arg];
	for (CSQuickActionsButton *button in [self rightButtons])
		if ([button.bundleID isEqualToString:@"com.apple.flashlight"])
			[button setSelected:arg];
}

%new
-(void)setDoNotDisturb:(BOOL)state
{
	DNDModeAssertionService *assertionService = (DNDModeAssertionService *)[objc_getClass("DNDModeAssertionService") serviceForClientIdentifier:@"com.apple.donotdisturb.control-center.module"];

	if (state) {
		DNDModeAssertionDetails *newAssertion = [objc_getClass("DNDModeAssertionDetails") userRequestedAssertionDetailsWithIdentifier:@"com.apple.control-center.manual-toggle" modeIdentifier:@"com.apple.donotdisturb.mode.default" lifetime:nil];
		[assertionService takeModeAssertionWithDetails:newAssertion error:NULL];
	} else {
		[assertionService invalidateAllActiveModeAssertionsWithError:NULL];
	}

	[[NSNotificationCenter defaultCenter] postNotificationName:@"SBQuietModeStatusChangedNotification" object:nil];
}

%new
-(BOOL)isDNDActive
{

	// DNDStateService *stateService = (DNDStateService *)[objc_getClass("DNDStateService") serviceForClientIdentifier:@"com.apple.donotdisturb.control-center.module"];
    return [[self.stateService queryCurrentStateWithError:nil] isActive];
}

%new
-(void)updateDND:(NSNotification *)notif
{
    BOOL active = [self isDNDActive];

	if ([self.flashlightButton.bundleID isEqualToString:@"com.apple.donotdisturb"])
		[self.flashlightButton setSelected:active];
	if ([self.cameraButton.bundleID isEqualToString:@"com.apple.donotdisturb"])
		[self.cameraButton setSelected:active];
	for (CSQuickActionsButton *button in [self leftButtons])
		if ([button.bundleID isEqualToString:@"com.apple.donotdisturb"])
			[button setSelected:active];
	for (CSQuickActionsButton *button in [self rightButtons])
		if ([button.bundleID isEqualToString:@"com.apple.donotdisturb"])
			[button setSelected:active];
}

%new
-(void)stateService:(id)arg1 didReceiveDoNotDisturbStateUpdate:(id)arg2
{
	dispatch_async(dispatch_get_main_queue(), ^{
		[self updateDND:nil];
	});
}

%end

%hook CSQuickActionsButton

%property (nonatomic, retain) NSString *bundleID;

-(void)setImage:(UIImage *)img
{
	%orig;
	[[self valueForKey:@"_contentView"] setImage:img];
}

-(void)setBundleID:(NSString*)bundleID
{
	%orig;
	if ([bundleID isEqualToString:@"com.apple.camera"]) {
		[self setImage:[self _imageWithName:@"OrbCamera"]];
		[self setLatching:FALSE];
	} else if ([bundleID isEqualToString:@"com.apple.flashlight"]) {
		[self setImage:[self _imageWithName:@"OrbFlashlightOff"]];
		[self setSelectedImage:[self _imageWithName:@"OrbFlashlightOff"]];
		[self setLatching:TRUE];
	} else if (([bundleID isEqualToString:@"com.apple.donotdisturb"])) {
		UIImageSymbolConfiguration *imageConfig = [UIImageSymbolConfiguration configurationWithTextStyle:UIFontTextStyleTitle2];
		[self setImage:[UIImage systemImageNamed:@"moon.fill" withConfiguration:imageConfig]];
		[self setSelectedImage:[UIImage systemImageNamed:@"moon.fill" withConfiguration:imageConfig]];
		((UIImageView *)[self valueForKey:@"_contentView"]).contentMode = UIViewContentModeScaleAspectFit;
		[self setLatching:TRUE];
	} else {
		[self setImage:[UIImage _applicationIconImageForBundleIdentifier:bundleID format:0 scale:[UIScreen mainScreen].scale]];
		[self setLatching:FALSE];
	}
}

-(void)setPermitted:(BOOL)permitted
{
	%orig(YES);
}

%end

%hook CSQuickActionsViewController

-(BOOL)hasCamera
{
	return true;
}

-(BOOL)hasFlashlight
{
	return true;
}

+(BOOL)deviceSupportsButtons
{
	return true;
}

%end

// vim: filetype=logos