diff --git a/assistant+/.theos/packages/com.zaid.assistant+-1.0.1 b/assistant+/.theos/packages/com.zaid.assistant+-1.0.1 index d8263ee..4800c7d 100644 --- a/assistant+/.theos/packages/com.zaid.assistant+-1.0.1 +++ b/assistant+/.theos/packages/com.zaid.assistant+-1.0.1 @@ -1 +1 @@ -2 \ No newline at end of file +58 \ No newline at end of file diff --git a/assistant+/Assistant+.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/assistant+/Assistant+.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate index 45b6ddf..8bb1e6f 100644 Binary files a/assistant+/Assistant+.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate and b/assistant+/Assistant+.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/assistant+/_/Applications/AssistantPlusApp.app/AssistantPlusApp b/assistant+/_/Applications/AssistantPlusApp.app/AssistantPlusApp index f8b6680..89053cb 100755 Binary files a/assistant+/_/Applications/AssistantPlusApp.app/AssistantPlusApp and b/assistant+/_/Applications/AssistantPlusApp.app/AssistantPlusApp differ diff --git a/assistant+/_/Library/MobileSubstrate/DynamicLibraries/Assistant+.dylib b/assistant+/_/Library/MobileSubstrate/DynamicLibraries/Assistant+.dylib index aa11214..8c29f18 100755 Binary files a/assistant+/_/Library/MobileSubstrate/DynamicLibraries/Assistant+.dylib and b/assistant+/_/Library/MobileSubstrate/DynamicLibraries/Assistant+.dylib differ diff --git a/assistant+/_/Library/MobileSubstrate/DynamicLibraries/AssistantPlusPluginManager.dylib b/assistant+/_/Library/MobileSubstrate/DynamicLibraries/AssistantPlusPluginManager.dylib index 3abb91a..1cb815e 100755 Binary files a/assistant+/_/Library/MobileSubstrate/DynamicLibraries/AssistantPlusPluginManager.dylib and b/assistant+/_/Library/MobileSubstrate/DynamicLibraries/AssistantPlusPluginManager.dylib differ diff --git a/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 94b2795..70d33ea 100644 --- a/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -1,4 +1,7 @@ + + diff --git a/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/xcshareddata/assistantplus_root_helper.xccheckout b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/xcshareddata/assistantplus_root_helper.xccheckout new file mode 100644 index 0000000..96c0a04 --- /dev/null +++ b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/xcshareddata/assistantplus_root_helper.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + EC9F4657-A65C-4A72-975C-EE3D32554A76 + IDESourceControlProjectName + assistantplus_root_helper + IDESourceControlProjectOriginsDictionary + + 1DDFB15FDAC578A4447EF06D6A0EB8914E453AB8 + https://bitbucket.org/ZaidElkurdi/assistantplus.git + + IDESourceControlProjectPath + assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + 1DDFB15FDAC578A4447EF06D6A0EB8914E453AB8 + ../../../.. + + IDESourceControlProjectURL + https://bitbucket.org/ZaidElkurdi/assistantplus.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + 1DDFB15FDAC578A4447EF06D6A0EB8914E453AB8 + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 1DDFB15FDAC578A4447EF06D6A0EB8914E453AB8 + IDESourceControlWCCName + Assistant2 + + + + diff --git a/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..9fa15d2 Binary files /dev/null and b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/assistant+/assistantplusapp/APCaptureGroupCommand.h b/assistant+/assistantplusapp/APCaptureGroupCommand.h new file mode 100644 index 0000000..bdc05aa --- /dev/null +++ b/assistant+/assistantplusapp/APCaptureGroupCommand.h @@ -0,0 +1,22 @@ +// +// APCaptureGroupCommand.h +// AssistantPlusApp +// +// Created by Zaid Elkurdi on 4/14/15. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// + +#import + +@interface APCaptureGroupCommand : NSObject +@property (nonatomic, strong) NSString *name; +@property (nonatomic, strong) NSArray *variables; +@property (nonatomic, strong) NSArray *conditionals; +@property (nonatomic, strong) NSString *trigger; +@property (nonatomic, strong) NSString *command; +@property (nonatomic, strong) NSString *uuid; + +-(id)initWithDictionary:(NSDictionary*)dict; +- (NSDictionary*)dictionaryRepresentation; + +@end diff --git a/assistant+/assistantplusapp/APCaptureGroupCommand.m b/assistant+/assistantplusapp/APCaptureGroupCommand.m new file mode 100644 index 0000000..3a109b5 --- /dev/null +++ b/assistant+/assistantplusapp/APCaptureGroupCommand.m @@ -0,0 +1,41 @@ +// +// APCaptureGroupCommand.m +// AssistantPlusApp +// +// Created by Zaid Elkurdi on 4/14/15. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// + +#import "APCaptureGroupCommand.h" + +@implementation APCaptureGroupCommand + +-(id)initWithDictionary:(NSDictionary*)dict { + if (self = [super init]) { + NSString *name = dict[@"name"]; + NSArray *variables = dict[@"variables"]; + NSArray *conditionals = dict[@"conditionals"]; + NSString *command = dict[@"command"]; + NSString *trigger = dict[@"trigger"]; + NSString *uuid = dict[@"uuid"]; + + self.name = name; + self.variables = variables ? variables : [NSArray array]; + self.conditionals = conditionals ? conditionals : [NSArray array]; + self.command = command ? command : @""; + self.trigger = trigger ? trigger : @""; + self.uuid = uuid ? uuid : [NSString stringWithFormat:@"%@", [NSUUID UUID]]; + } + return self; +} + +- (NSDictionary*)dictionaryRepresentation { + return @{@"name" : self.name ? self.name : @"Untitled", + @"variables" : self.variables ? self.variables : [NSArray array], + @"conditionals" : self.conditionals ? self.conditionals : [NSArray array], + @"command" : self.command ? self.command : @"", + @"trigger" : self.trigger ? self.trigger : @"", + @"uuid" : self.uuid ? self.uuid : [NSString stringWithFormat:@"%@", [NSUUID UUID]]}; +} + +@end diff --git a/assistant+/assistantplusapp/ActivatorListenersViewController.h b/assistant+/assistantplusapp/ActivatorListenersViewController.h index f548766..b7e0910 100644 --- a/assistant+/assistantplusapp/ActivatorListenersViewController.h +++ b/assistant+/assistantplusapp/ActivatorListenersViewController.h @@ -3,7 +3,7 @@ // AssistantPlusApp // // Created by Zaid Elkurdi on 3/22/15. -// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserve. // #import diff --git a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.pbxproj b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.pbxproj index 2db4f3b..b212fc4 100644 --- a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.pbxproj +++ b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.pbxproj @@ -20,6 +20,12 @@ B86E67181ACE7AC0007C6014 /* APCustomReply.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0AE51AC32CEF00D4D107 /* APCustomReply.m */; }; B86E67191ACE7AC0007C6014 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0A871ABF7D6200D4D107 /* main.m */; }; B86E671B1ACE7C66007C6014 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B86E671A1ACE7C66007C6014 /* Default-568h@2x.png */; }; + B8B2D2411ADD875900FEE8C3 /* CaptureGroupCommandsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8B2D2401ADD875900FEE8C3 /* CaptureGroupCommandsViewController.m */; }; + B8B2D2471ADD879500FEE8C3 /* APCaptureGroupCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = B8B2D2461ADD879500FEE8C3 /* APCaptureGroupCommand.m */; }; + B8B2D24A1ADD87AB00FEE8C3 /* CaptureGroupCommandDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8B2D2491ADD87AB00FEE8C3 /* CaptureGroupCommandDetailViewController.m */; }; + B8B2D24B1ADD8EE900FEE8C3 /* CaptureGroupCommandsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8B2D2401ADD875900FEE8C3 /* CaptureGroupCommandsViewController.m */; }; + B8B2D24C1ADD8EED00FEE8C3 /* CaptureGroupCommandDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8B2D2491ADD87AB00FEE8C3 /* CaptureGroupCommandDetailViewController.m */; }; + B8B2D24D1ADD8EF100FEE8C3 /* APCaptureGroupCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = B8B2D2461ADD879500FEE8C3 /* APCaptureGroupCommand.m */; }; B8EC46D41AC6422100ED3836 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0A7C1ABF79AA00D4D107 /* AppDelegate.m */; }; B8EC46D51AC6422100ED3836 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0A7F1ABF79AA00D4D107 /* MainViewController.m */; }; B8EC46D61AC6422100ED3836 /* ActivatorListenersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0ACD1ABF8F7C00D4D107 /* ActivatorListenersViewController.m */; }; @@ -82,6 +88,12 @@ B88C0AE21AC32C2A00D4D107 /* CustomReplyDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomReplyDetailViewController.m; sourceTree = SOURCE_ROOT; }; B88C0AE41AC32CEF00D4D107 /* APCustomReply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APCustomReply.h; sourceTree = SOURCE_ROOT; }; B88C0AE51AC32CEF00D4D107 /* APCustomReply.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APCustomReply.m; sourceTree = SOURCE_ROOT; }; + B8B2D23F1ADD875900FEE8C3 /* CaptureGroupCommandsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CaptureGroupCommandsViewController.h; sourceTree = SOURCE_ROOT; }; + B8B2D2401ADD875900FEE8C3 /* CaptureGroupCommandsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CaptureGroupCommandsViewController.m; sourceTree = SOURCE_ROOT; }; + B8B2D2451ADD879500FEE8C3 /* APCaptureGroupCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APCaptureGroupCommand.h; sourceTree = SOURCE_ROOT; }; + B8B2D2461ADD879500FEE8C3 /* APCaptureGroupCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APCaptureGroupCommand.m; sourceTree = SOURCE_ROOT; }; + B8B2D2481ADD87AB00FEE8C3 /* CaptureGroupCommandDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CaptureGroupCommandDetailViewController.h; sourceTree = SOURCE_ROOT; }; + B8B2D2491ADD87AB00FEE8C3 /* CaptureGroupCommandDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CaptureGroupCommandDetailViewController.m; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -174,6 +186,10 @@ B88C0A7E1ABF79AA00D4D107 /* MainViewController.h */, B88C0A7F1ABF79AA00D4D107 /* MainViewController.m */, B834EFF31AC5EF2C00CF009E /* LaunchScreen2.xib */, + B8B2D23F1ADD875900FEE8C3 /* CaptureGroupCommandsViewController.h */, + B8B2D2401ADD875900FEE8C3 /* CaptureGroupCommandsViewController.m */, + B8B2D2481ADD87AB00FEE8C3 /* CaptureGroupCommandDetailViewController.h */, + B8B2D2491ADD87AB00FEE8C3 /* CaptureGroupCommandDetailViewController.m */, B86E66D81ACD1991007C6014 /* PluginsViewController.h */, B86E66D91ACD1991007C6014 /* PluginsViewController.m */, B88C0ADE1AC32C2000D4D107 /* CustomRepliesViewController.h */, @@ -188,6 +204,8 @@ B88C0AD21ABF968B00D4D107 /* APActivatorListener.m */, B88C0AE41AC32CEF00D4D107 /* APCustomReply.h */, B88C0AE51AC32CEF00D4D107 /* APCustomReply.m */, + B8B2D2451ADD879500FEE8C3 /* APCaptureGroupCommand.h */, + B8B2D2461ADD879500FEE8C3 /* APCaptureGroupCommand.m */, B88C0A871ABF7D6200D4D107 /* main.m */, B88C0AB61ABF838500D4D107 /* Info.plist */, B88C0AC11ABF864D00D4D107 /* LaunchScreen.xib */, @@ -353,12 +371,15 @@ buildActionMask = 2147483647; files = ( B8EC46D41AC6422100ED3836 /* AppDelegate.m in Sources */, + B8B2D2411ADD875900FEE8C3 /* CaptureGroupCommandsViewController.m in Sources */, B8EC46D51AC6422100ED3836 /* MainViewController.m in Sources */, B8EC46D61AC6422100ED3836 /* ActivatorListenersViewController.m in Sources */, B8EC46D71AC6422100ED3836 /* CustomRepliesViewController.m in Sources */, + B8B2D24A1ADD87AB00FEE8C3 /* CaptureGroupCommandDetailViewController.m in Sources */, B8EC46D81AC6422100ED3836 /* CustomReplyDetailViewController.m in Sources */, B86E66DA1ACD1991007C6014 /* PluginsViewController.m in Sources */, B8EC46D91AC6422100ED3836 /* ListenerDetailViewController.m in Sources */, + B8B2D2471ADD879500FEE8C3 /* APCaptureGroupCommand.m in Sources */, B8EC46DA1AC6422100ED3836 /* APActivatorListener.m in Sources */, B8EC46DB1AC6422100ED3836 /* APCustomReply.m in Sources */, B8EC46DC1AC6422100ED3836 /* main.m in Sources */, @@ -378,12 +399,15 @@ buildActionMask = 2147483647; files = ( B86E67101ACE7AC0007C6014 /* AppDelegate.m in Sources */, + B8B2D24B1ADD8EE900FEE8C3 /* CaptureGroupCommandsViewController.m in Sources */, B86E67111ACE7AC0007C6014 /* MainViewController.m in Sources */, B86E67121ACE7AC0007C6014 /* PluginsViewController.m in Sources */, B86E67131ACE7AC0007C6014 /* CustomRepliesViewController.m in Sources */, B86E67141ACE7AC0007C6014 /* CustomReplyDetailViewController.m in Sources */, + B8B2D24C1ADD8EED00FEE8C3 /* CaptureGroupCommandDetailViewController.m in Sources */, B86E67151ACE7AC0007C6014 /* ActivatorListenersViewController.m in Sources */, B86E67161ACE7AC0007C6014 /* ListenerDetailViewController.m in Sources */, + B8B2D24D1ADD8EF100FEE8C3 /* APCaptureGroupCommand.m in Sources */, B86E67171ACE7AC0007C6014 /* APActivatorListener.m in Sources */, B86E67181ACE7AC0007C6014 /* APCustomReply.m in Sources */, B86E67191ACE7AC0007C6014 /* main.m in Sources */, diff --git a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate index 31a1286..bfc28ab 100644 Binary files a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate and b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/WorkspaceSettings.xcsettings b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..659c876 --- /dev/null +++ b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,10 @@ + + + + + HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges + + SnapshotAutomaticallyBeforeSignificantChanges + + + diff --git a/assistant+/assistantplusapp/CaptureGroupCommandDetailViewController.h b/assistant+/assistantplusapp/CaptureGroupCommandDetailViewController.h new file mode 100644 index 0000000..167f53c --- /dev/null +++ b/assistant+/assistantplusapp/CaptureGroupCommandDetailViewController.h @@ -0,0 +1,19 @@ +// +// CaptureGroupCommandDetailViewController.h +// AssistantPlusApp +// +// Created by Zaid Elkurdi on 4/14/15. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// + +#import +#import "APCaptureGroupCommand.h" + +@protocol CommandDetailDelegate +- (void)commandDidChange:(APCaptureGroupCommand*)command; +@end + +@interface CaptureGroupCommandDetailViewController : UIViewController +@property (assign) id delegate; +- (id)initWithCommand:(APCaptureGroupCommand*)command; +@end diff --git a/assistant+/assistantplusapp/CaptureGroupCommandDetailViewController.m b/assistant+/assistantplusapp/CaptureGroupCommandDetailViewController.m new file mode 100644 index 0000000..ac73e40 --- /dev/null +++ b/assistant+/assistantplusapp/CaptureGroupCommandDetailViewController.m @@ -0,0 +1,487 @@ +// +// CaptureGroupCommandDetailViewController.m +// AssistantPlusApp +// +// Created by Zaid Elkurdi on 4/14/15. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// + +#import "CaptureGroupCommandDetailViewController.h" + +typedef enum { + APNameCell = 1, + APTriggerCell = 2, + APCommandCell = 3 +} APTextCellType; + +typedef enum { + APWhenValue = 1, + APSetValue = 2, +} APConditionalEditingType; + +@interface CaptureGroupCommandDetailViewController () +@property (strong, nonatomic) APCaptureGroupCommand *currCommand; +@property (strong, nonatomic) UITextField *nameField; +@property (strong, nonatomic) UITextField *triggerField; +@property (strong, nonatomic) UITextField *commandField; +@property (strong, nonatomic) UITableView *tableView; + +@property (strong, nonatomic) NSMutableArray *mutableVariables; +@property (strong, nonatomic) NSMutableArray *mutableConditionals; +@property (nonatomic) BOOL didChange; +@property (nonatomic) NSInteger conditionalToEdit; +@property (nonatomic) APConditionalEditingType editingType; +@end + +@implementation CaptureGroupCommandDetailViewController + +- (id)initWithCommand:(APCaptureGroupCommand*)command { + if (self = [super init]) { + self.currCommand = command; + self.mutableVariables = command.variables ? [command.variables mutableCopy] : [NSMutableArray array]; + + if (command.conditionals && command.conditionals.count > 0) { + self.mutableConditionals = [[NSMutableArray alloc] init]; + for (NSArray *currConditional in command.conditionals) { + [self.mutableConditionals addObject:[currConditional mutableCopy]]; + } + } else { + self.mutableConditionals = [NSMutableArray array]; + } + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + UIColor *backgroundColor = [UIColor colorWithWhite:.9f alpha:1.0]; + self.view.backgroundColor = backgroundColor; + + self.tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleGrouped]; + self.tableView.dataSource = self; + self.tableView.delegate = self; + + CGFloat expectedHeight = [[self getHelpMessage] boundingRectWithSize:CGSizeMake(self.view.frame.size.width-20, CGFLOAT_MAX) + options:NSStringDrawingUsesLineFragmentOrigin + attributes:@{NSFontAttributeName : [UIFont fontWithName:@"Helvetica" size:14]} + context:nil].size.height; + + UIView *msgView = [[UIView alloc] initWithFrame:CGRectMake(0, 10, self.view.frame.size.width, expectedHeight+30)]; + UILabel *msgLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, self.view.frame.size.width-20, expectedHeight)]; + msgLabel.lineBreakMode = NSLineBreakByWordWrapping; + msgLabel.numberOfLines = 0; + msgLabel.text = [self getHelpMessage]; + msgLabel.textAlignment = NSTextAlignmentLeft; + msgLabel.font = [UIFont fontWithName:@"Helvetica" size:14]; + msgLabel.textColor = [UIColor darkGrayColor]; + [msgView addSubview:msgLabel]; + + self.tableView.tableFooterView = msgView; + [self.view addSubview:self.tableView]; + + UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addPressed:)]; + [self.navigationItem setRightBarButtonItem:addButton]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(saveChangesIfNecessary) + name:UIApplicationWillResignActiveNotification + object:nil]; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [self saveChangesIfNecessary]; +} + +- (void)saveChangesIfNecessary { + if (self.didChange) { + self.currCommand.conditionals = self.mutableConditionals; + self.currCommand.trigger = self.triggerField.text; + self.currCommand.variables = self.mutableVariables; + self.currCommand.name = self.nameField.text; + self.currCommand.command = self.commandField.text; + [self.delegate commandDidChange:self.currCommand]; + } +} + +#pragma mark - UITableViewDataSource + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 5; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if (section == 0) { + return 1; + } else if (section == 1) { + return 1; + } else if (section == 2) { + return self.mutableVariables.count; + } else if (section == 3) { + return self.mutableConditionals.count; + } else { + return 1; + } +} + +- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 0) { + return [self createTextCell:APNameCell]; + } else if (indexPath.section == 1) { + return [self createTextCell:APTriggerCell]; + } else if (indexPath.section == 2) { + return [self createVariableCellForIndex:indexPath.row]; + } else if (indexPath.section == 3) { + return [self createConditionalCellForIndex:indexPath.row]; + } else if (indexPath.section == 4) { + return [self createTextCell:APCommandCell]; + } + + return nil; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 3) { + return 200; + } + + return 50; +} + +- (NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { + switch (section) { + case 0: + return @""; + break; + case 1: + return @""; + case 2: + return @"Variables"; + case 3: + return @"Conditionals"; + case 4: + return @"Command"; + default: + return @""; + } +} + +#pragma mark - Cell Helpers + +- (UITableViewCell*)createTextCell:(APTextCellType)cellType { + UITableViewCell *cell = [[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + + UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 60, 50)]; + + UITextField *nameField = [[UITextField alloc] initWithFrame:CGRectMake(80, 2, self.view.frame.size.width-60, 50)]; + nameField.delegate = self; + nameField.tag = -1; + nameField.returnKeyType = UIReturnKeyDone; + + if (cellType == APNameCell) { + nameLabel.text = @"Name:"; + nameField.text = self.currCommand.name; + self.nameField = nameField; + + CGRect oldFrame = self.nameField.frame; + oldFrame.origin.x = 70; + oldFrame.size.width = self.view.frame.size.width - 70; + nameField.frame = oldFrame; + self.nameField.frame = oldFrame; + } else if (cellType == APTriggerCell) { + nameLabel.text = @"Trigger:"; + nameField.text = self.currCommand.trigger; + self.triggerField = nameField; + } else if (cellType == APCommandCell) { + nameLabel.hidden = YES; + nameField.text = self.currCommand.command; + self.commandField = nameField; + + CGRect oldFrame = self.commandField.frame; + oldFrame.origin.x = 5; + oldFrame.size.width = self.view.frame.size.width - 5; + self.commandField.frame = oldFrame; + } + + [cell.contentView addSubview:nameLabel]; + [cell.contentView addSubview:nameField]; + return cell; +} + +- (UITableViewCell*)createVariableCellForIndex:(NSInteger)index { + UITableViewCell *cell = [[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + + UITextField *nameField = [[UITextField alloc] initWithFrame:CGRectMake(10, 2, self.view.frame.size.width-10, 50)]; + nameField.delegate = self; + nameField.tag = 100+index; + nameField.returnKeyType = UIReturnKeyDone; + nameField.placeholder = @"Unnamed Variable"; + + NSString *variableName = [self.mutableVariables objectAtIndex:index]; + if (variableName.length > 0) { + nameField.text = variableName; + } else { + nameField.text = @""; + } + + [cell.contentView addSubview:nameField]; + + return cell; +} + +- (UITableViewCell*)createConditionalCellForIndex:(NSInteger)index { + UITableViewCell *cell = [[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + + UILabel *whenlabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 70, 50)]; + whenlabel.text = @"When:"; + + UILabel *equalsLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 50, 70, 50)]; + equalsLabel.text = @"Equals:"; + + UILabel *setLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 100, 70, 50)]; + setLabel.text = @"Set:"; + + UILabel *toLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 150, 70, 50)]; + toLabel.text = @"To:"; + + UIButton *whenField = [UIButton buttonWithType:UIButtonTypeCustom]; + whenField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; + whenField.frame = CGRectMake(70, 2, self.view.frame.size.width-15, 50); + whenField.tag = index; + [whenField addTarget:self action:@selector(whenButtonPressedForIndex:) forControlEvents:UIControlEventTouchUpInside]; + + NSString *whenVariableName = self.mutableConditionals[index][0]; + if (whenVariableName && whenVariableName.length > 0) { + [whenField setTitle:whenVariableName forState:UIControlStateNormal]; + [whenField setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + } else { + [whenField setTitle:@"Select Variable..." forState:UIControlStateNormal]; + [whenField setTitleColor:[UIColor colorWithWhite:0.7 alpha:1.0] forState:UIControlStateNormal]; + } + + UITextField *equalsField = [[UITextField alloc] initWithFrame:CGRectMake(80, 57, self.view.frame.size.width-80, 35)]; + equalsField.returnKeyType = UIReturnKeyDone; + equalsField.font = [UIFont systemFontOfSize:17]; + equalsField.tag = 1000 + index; + equalsField.placeholder = @"A Value"; + + NSString *equalsValue = self.mutableConditionals[index][1]; + if (equalsValue.length > 0) { + equalsField.text = equalsValue; + } else { + equalsField.text = @""; + } + equalsField.delegate = self; + + UIButton *setField = [UIButton buttonWithType:UIButtonTypeCustom]; + setField.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; + setField.frame = CGRectMake(50, 100, self.view.frame.size.width-75, 50); + setField.tag = index; + [setField addTarget:self action:@selector(setButtonPressedForIndex:) forControlEvents:UIControlEventTouchUpInside]; + + NSString *setVariableName = self.mutableConditionals[index][2]; + if (setVariableName && setVariableName.length > 0) { + [setField setTitle:setVariableName forState:UIControlStateNormal]; + [setField setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + } else { + [setField setTitle:@"Select Variable..." forState:UIControlStateNormal]; + [setField setTitleColor:[UIColor colorWithWhite:0.7 alpha:1.0] forState:UIControlStateNormal]; + } + + UITextField *toField = [[UITextField alloc] initWithFrame:CGRectMake(40, 158, self.view.frame.size.width-80, 35)]; + toField.returnKeyType = UIReturnKeyDone; + toField.tag = 2000 + index; + toField.font = [UIFont systemFontOfSize:17]; + toField.placeholder = @"A Value"; + + NSString *toValue = self.mutableConditionals[index][3]; + if (toValue.length > 0) { + toField.text = toValue; + } else { + toField.text = @""; + } + toField.delegate = self; + + [cell.contentView addSubview:whenlabel]; + [cell.contentView addSubview:equalsLabel]; + [cell.contentView addSubview:setLabel]; + [cell.contentView addSubview:toLabel]; + + [cell.contentView addSubview:equalsField]; + [cell.contentView addSubview:toField]; + [cell.contentView addSubview:whenField]; + [cell.contentView addSubview:setField]; + + return cell; +} + +#pragma mark - UITableViewDelegate + +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 0 || indexPath.section == 1 || indexPath.section == 4) { + return NO; + } + return YES; +} + +- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { + return UITableViewCellEditingStyleDelete; +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { + if (editingStyle == UITableViewCellEditingStyleDelete) { + + if (indexPath.section == 2) { + [self.mutableVariables removeObjectAtIndex:indexPath.row]; + } else if (indexPath.section == 3) { + [self.mutableConditionals removeObjectAtIndex:indexPath.row]; + } + + [self.tableView beginUpdates]; + [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.tableView endUpdates]; + self.didChange = YES; + } +} + +#pragma mark - UI Delegates + +- (void)addPressed:(id)sender { + UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Add new..." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:nil, nil]; + [actionSheet addButtonWithTitle:@"Variable"]; + [actionSheet addButtonWithTitle:@"Conditional"]; + actionSheet.cancelButtonIndex = 0; + actionSheet.tag = 1; + [actionSheet showInView:self.view]; +} + +- (void)whenButtonPressedForIndex:(UIButton*)button { + self.conditionalToEdit = button.tag; + self.editingType = APWhenValue; + [self displayVariableSelection]; +} + +- (void)setButtonPressedForIndex:(UIButton*)button { + self.conditionalToEdit = button.tag; + self.editingType = APSetValue; + [self displayVariableSelection]; +} + +- (void)displayVariableSelection { + UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Select Variable..." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:nil, nil]; + for (NSString *name in self.mutableVariables) { + if (name.length > 0) { + [actionSheet addButtonWithTitle:name]; + } + } + actionSheet.tag = 2; + actionSheet.cancelButtonIndex = 0; + [actionSheet showInView:self.view]; +} + +- (void)addOptionsSheetClickedButtonAtIndex:(NSInteger)buttonIndex { + NSInteger section; + NSInteger row; + if (buttonIndex == 1) { + //variable + section = 2; + [self.mutableVariables addObject:@""]; + row = self.mutableVariables.count-1; + + } else if (buttonIndex == 2) { + //conditional + section = 3; + [self.mutableConditionals addObject:[NSMutableArray arrayWithObjects:@"", @"", @"", @"", nil]]; + row = self.mutableConditionals.count-1; + } else { + //Is cancel button + return; + } + + NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:row inSection:section]; + + [self.tableView beginUpdates]; + [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.tableView endUpdates]; +} + +- (void)variableSelectionSheetClickedButtonAtIndex:(NSInteger)buttonIndex { + NSString *variableName = [self.mutableVariables objectAtIndex:buttonIndex-1]; + if (self.editingType == APSetValue) { + [[self.mutableConditionals objectAtIndex:self.conditionalToEdit] setObject:variableName atIndexedSubscript:2]; + } else if (self.editingType == APWhenValue) { + [[self.mutableConditionals objectAtIndex:self.conditionalToEdit] setObject:variableName atIndexedSubscript:0]; + } + + [self.tableView beginUpdates]; + [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForItem:self.conditionalToEdit inSection:3]] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.tableView endUpdates]; +} + +- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { + if (buttonIndex == 0) { + return; + } + + if (actionSheet.tag == 1) { + [self addOptionsSheetClickedButtonAtIndex:buttonIndex]; + } else if (actionSheet.tag == 2) { + [self variableSelectionSheetClickedButtonAtIndex:buttonIndex]; + } + [self.view endEditing:YES]; + self.didChange = YES; + + } + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [textField resignFirstResponder]; + return NO; +} + +- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { + if (textField == self.commandField) { + [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:4] atScrollPosition:UITableViewScrollPositionTop animated:YES]; + } else if (textField == self.nameField) { + [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES]; + } else if (textField == self.triggerField) { + [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; + } + return YES; +} + +- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { + self.didChange = YES; + NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string]; + if (textField.tag - 2000 >= 0) { + [[self.mutableConditionals objectAtIndex:textField.tag-2000] setObject:newText atIndexedSubscript:3]; + } else if (textField.tag - 1000 >= 0) { + [[self.mutableConditionals objectAtIndex:textField.tag-1000] setObject:newText atIndexedSubscript:1]; + } else if (textField.tag - 100 >= 0) { + [self.mutableVariables setObject:newText atIndexedSubscript:textField.tag-100]; + } else if (textField == self.nameField) { + self.currCommand.name = newText; + } else if (textField == self.triggerField) { + self.currCommand.trigger = newText; + } + return YES; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + +#pragma mark - Helpers + +- (NSString*)getHelpMessage { + return @"Enabled: For a listener to appear in Activator it must be enabled\n" + "\nPassthrough: If enabled, Assistant+ will continue searching for another Activator listener, custom reply, or plugin to handle the command. If it doesn't find anything to handle the command, Siri will go to its default action. You can add a voice confirmation for your Activator listener if you create a custom reply for the same trigger and then enable passthrough for your listener\n" + "\nName: A name to describe your listener. This can be anything and is purely for informational purposes\n" + "\nTrigger: The command that will trigger the Activator listener, you must have at least one for the listener to appear in Activator. You may use wildcards in the trigger by using (.*). For example, '(.*)turn on the lights' will trigger on \"Turn on the lights\", \"Siri turn on the lights\", \"Hey Siri please turn on the lights\", etc.\n" + "\nOnce you create an Activator listener here you must go to Activator and assign it to an event."; +} + +@end diff --git a/assistant+/assistantplusapp/CaptureGroupCommandsViewController.h b/assistant+/assistantplusapp/CaptureGroupCommandsViewController.h new file mode 100644 index 0000000..4c81355 --- /dev/null +++ b/assistant+/assistantplusapp/CaptureGroupCommandsViewController.h @@ -0,0 +1,14 @@ +// +// CaptureGroupCommandsViewController.h +// AssistantPlusApp +// +// Created by Zaid Elkurdi on 4/14/15. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// + +#import +#import "CaptureGroupCommandDetailViewController.h" + +@interface CaptureGroupCommandsViewController : UIViewController + +@end diff --git a/assistant+/assistantplusapp/CaptureGroupCommandsViewController.m b/assistant+/assistantplusapp/CaptureGroupCommandsViewController.m new file mode 100644 index 0000000..2fe8918 --- /dev/null +++ b/assistant+/assistantplusapp/CaptureGroupCommandsViewController.m @@ -0,0 +1,168 @@ +// +// CaptureGroupCommandsViewController.m +// AssistantPlusApp +// +// Created by Zaid Elkurdi on 4/14/15. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// + +#import "CaptureGroupCommandsViewController.h" +#import "APCaptureGroupCommand.h" +#import "CPDistributedMessagingCenter.h" + +@interface CaptureGroupCommandsViewController () +@property (strong, nonatomic) UITableView *commandsTable; +@end + +@implementation CaptureGroupCommandsViewController { + NSMutableArray *savedCommands; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.title = @"Commands"; + + UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewCommand:)]; + [self.navigationItem setRightBarButtonItem:addButton]; + + self.commandsTable = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain]; + self.commandsTable.delegate = self; + self.commandsTable.dataSource = self; + self.commandsTable.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; + + UIColor *backgroundColor = [UIColor colorWithWhite:.9f alpha:1.0]; + self.commandsTable.backgroundColor = backgroundColor; + + [self.view addSubview:self.commandsTable]; + + [self loadCommandsFromFile]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + if ([self.commandsTable indexPathForSelectedRow]) { + [self.commandsTable deselectRowAtIndexPath:[self.commandsTable indexPathForSelectedRow] animated:YES]; + } +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + +- (void)loadCommandsFromFile { + savedCommands = [[NSMutableArray alloc] init]; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if ([defaults objectForKey:@"captureGroupCommands"]) { + NSArray *commands = [defaults objectForKey:@"captureGroupCommands"]; + for (NSDictionary *currCommand in commands) { + APCaptureGroupCommand *command = [[APCaptureGroupCommand alloc] initWithDictionary:currCommand]; + [savedCommands addObject:command]; + } + } +} + +- (void)saveCommandsToFile { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSMutableArray *toSave = [[NSMutableArray alloc] init]; + if (savedCommands) { + for (APCaptureGroupCommand *currCommand in savedCommands) { + [toSave addObject:[currCommand dictionaryRepresentation]]; + } + [defaults setObject:toSave forKey:@"captureGroupCommands"]; + [defaults synchronize]; + } + +#if !(TARGET_IPHONE_SIMULATOR) + NSLog(@"Sending back to springobard!"); + CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"]; + [center sendMessageName:@"UpdateCaptureGroupCommands" userInfo:@{@"captureGroupCommands" : toSave}]; +#endif +} + +#pragma mark - Button Handlers + +- (void)addNewCommand:(id)sender { + APCaptureGroupCommand *newCommand = [[APCaptureGroupCommand alloc] init]; + [savedCommands addObject:newCommand]; + + NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:savedCommands.count-1 inSection:0]; + + [CATransaction begin]; + [self.commandsTable beginUpdates]; + [CATransaction setCompletionBlock: ^{ + [self tableView:self.commandsTable didSelectRowAtIndexPath:newIndexPath]; + }]; + [self.commandsTable insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.commandsTable endUpdates]; + [CATransaction commit]; + +} + +#pragma mark - UITableViewDataSource + +- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"commandCell"]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"commandCell"]; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + } + + APCaptureGroupCommand *currCommand = [savedCommands objectAtIndex:indexPath.row]; + cell.textLabel.text = currCommand.name.length > 0 ? currCommand.name : @"Untitled Command"; + cell.detailTextLabel.text = currCommand.trigger.length > 0 ? currCommand.trigger : @"No Trigger Yet"; + + return cell; +} + +#pragma mark - UITableViewDelegate + +-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + APCaptureGroupCommand *selectedCommand = [savedCommands objectAtIndex:indexPath.row]; + CaptureGroupCommandDetailViewController *detailVC = [[CaptureGroupCommandDetailViewController alloc] initWithCommand:selectedCommand]; + detailVC.delegate = self; + [self.navigationController pushViewController:detailVC animated:YES]; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return savedCommands.count; +} + +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { + return YES; +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { + if (editingStyle == UITableViewCellEditingStyleDelete) { + APCaptureGroupCommand *toDelete = [savedCommands objectAtIndex:indexPath.row]; + [savedCommands removeObject:toDelete]; + [self saveCommandsToFile]; + + [self.commandsTable beginUpdates]; + [self.commandsTable deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + [self.commandsTable endUpdates]; + } +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 60; +} + +#pragma mark - CommandDelegate + +- (void)commandDidChange:(APCaptureGroupCommand *)command{ + for (NSInteger currIndex = 0; currIndex < savedCommands.count; currIndex++) { + APCaptureGroupCommand *currCommand = [savedCommands objectAtIndex:currIndex]; + if ([currCommand.uuid isEqualToString:command.uuid]) { + NSLog(@"Replacing %@", currCommand); + [savedCommands replaceObjectAtIndex:currIndex withObject:currCommand]; + break; + } + } + + [self saveCommandsToFile]; + [self.commandsTable reloadData]; +} + +@end diff --git a/assistant+/assistantplusapp/MainViewController.m b/assistant+/assistantplusapp/MainViewController.m index ea7ece9..d4d1070 100644 --- a/assistant+/assistantplusapp/MainViewController.m +++ b/assistant+/assistantplusapp/MainViewController.m @@ -3,6 +3,7 @@ #import "CustomRepliesViewController.h" #import "CPDistributedMessagingCenter.h" #import "PluginsViewController.h" +#import "CaptureGroupCommandsViewController.h" @implementation MainViewController @@ -54,6 +55,9 @@ cellTitle = @"Custom Replies"; break; case 2: + cellTitle = @"Capture Group Commands"; + break; + case 3: cellTitle = @"Installed Plugins"; default: break; @@ -64,7 +68,7 @@ } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 3; + return 4; } - (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section { @@ -97,7 +101,10 @@ case 1: [self goToNewVC:[[CustomRepliesViewController alloc] init]]; break; - case 2: { + case 2: + [self goToNewVC:[[CaptureGroupCommandsViewController alloc] init]]; + break; + case 3: { #if !(TARGET_IPHONE_SIMULATOR) CPDistributedMessagingCenter *center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"]; NSDictionary *installed = [center sendMessageAndReceiveReplyName:@"getInstalledPlugins" userInfo:nil]; diff --git a/assistant+/assistantplusapp/Makefile b/assistant+/assistantplusapp/Makefile index dfd01e2..2581b65 100644 --- a/assistant+/assistantplusapp/Makefile +++ b/assistant+/assistantplusapp/Makefile @@ -5,7 +5,7 @@ export TARGET = iphone:clang:latest:8.0 export SDKVERSION=8.1 APPLICATION_NAME = AssistantPlusApp -AssistantPlusApp_FILES = main.m AppDelegate.m MainViewController.m APActivatorListener.m ActivatorListenersViewController.m ListenerDetailViewController.m CustomReplyDetailViewController.m CustomRepliesViewController.m APCustomReply.m PluginsViewController.m +AssistantPlusApp_FILES = main.m AppDelegate.m MainViewController.m APActivatorListener.m ActivatorListenersViewController.m ListenerDetailViewController.m CustomReplyDetailViewController.m CustomRepliesViewController.m APCustomReply.m PluginsViewController.m CaptureGroupCommandDetailViewController.m CaptureGroupCommandsViewController.m APCaptureGroupCommand.m AssistantPlusApp_FRAMEWORKS = UIKit CoreGraphics QuartzCore AssistantPlusApp_PRIVATE_FRAMEWORKS = AppSupport AssistantPlusApp_CFLAGS = -fobjc-arc diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/AssistantPlusApp b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/AssistantPlusApp index f8b6680..89053cb 100755 Binary files a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/AssistantPlusApp and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/AssistantPlusApp differ diff --git a/assistant+/assistantpluspluginmanager/APCaptureGroupCommand.h b/assistant+/assistantpluspluginmanager/APCaptureGroupCommand.h new file mode 100644 index 0000000..bbc59f2 --- /dev/null +++ b/assistant+/assistantpluspluginmanager/APCaptureGroupCommand.h @@ -0,0 +1,21 @@ +// +// APCaptureGroupCommand.h +// AssistantPlusApp +// +// Created by Zaid Elkurdi on 4/14/15. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// + +#import + +@interface APCaptureGroupCommand : NSObject +@property (nonatomic, strong) NSString *name; +@property (nonatomic, strong) NSArray *variables; +@property (nonatomic, strong) NSArray *conditionals; +@property (nonatomic, strong) NSRegularExpression *trigger; +@property (nonatomic, strong) NSString *command; +@property (nonatomic, strong) NSString *uuid; + +-(id)initWithDictionary:(NSDictionary*)dict; +-(NSString*)buildCommandWithValues:(NSArray*)values; +@end diff --git a/assistant+/assistantpluspluginmanager/APCaptureGroupCommand.m b/assistant+/assistantpluspluginmanager/APCaptureGroupCommand.m new file mode 100644 index 0000000..948cbf4 --- /dev/null +++ b/assistant+/assistantpluspluginmanager/APCaptureGroupCommand.m @@ -0,0 +1,108 @@ +// +// APCaptureGroupCommand.m +// AssistantPlusApp +// +// Created by Zaid Elkurdi on 4/14/15. +// Copyright (c) 2015 Zaid Elkurdi. All rights reserved. +// + +#import "APCaptureGroupCommand.h" + +@implementation APCaptureGroupCommand { + NSArray *variableRanges; +} + +-(id)initWithDictionary:(NSDictionary*)dict { + if (self = [super init]) { + NSString *name = dict[@"name"]; + NSArray *variables = dict[@"variables"]; + NSArray *conditionals = dict[@"conditionals"]; + NSString *command = dict[@"command"]; + NSString *trigger = dict[@"trigger"]; + NSString *uuid = dict[@"uuid"]; + + self.name = name; + self.variables = variables ? variables : [NSArray array]; + self.conditionals = conditionals ? conditionals : [NSArray array]; + self.command = command ? command : @""; + self.trigger = [NSRegularExpression regularExpressionWithPattern:trigger options:NSRegularExpressionCaseInsensitive error:nil]; + self.uuid = uuid ? uuid : [NSString stringWithFormat:@"%@", [NSUUID UUID]]; + [self buildRangeDictionary]; + } + return self; +} + +- (void)buildRangeDictionary { + NSMutableArray *newVariableRanges = [[NSMutableArray alloc] init]; + for (NSString *currVariable in self.variables) { + NSRange currRange = [self.command rangeOfString:[NSString stringWithFormat:@"[%@]", currVariable]]; + if (currRange.location != NSNotFound) { + [newVariableRanges addObject:@[currVariable, [NSValue valueWithRange:currRange]]]; + } + } + + //Sort the variables by location in order to calculate offset easily + variableRanges = [newVariableRanges sortedArrayUsingComparator:^NSComparisonResult(NSArray *first, NSArray *second) { + NSRange firstRange = [first[1] rangeValue]; + NSRange secondRange = [second[1] rangeValue]; + + return firstRange.location > secondRange.location; + }]; +} + +-(NSString*)buildCommandWithValues:(NSArray*)values { + //First, map the captured values to their variable names + NSMutableDictionary *variablesToValues = [[NSMutableDictionary alloc] init]; + NSInteger currIndex = 0; + NSLog(@"Values: %@", values); + for (NSString *currValue in values) { + NSLog(@"On: %@", currValue); + if (currIndex < self.variables.count) { + NSLog(@"Adding: %@", currValue); + NSString *currVariable = self.variables[currIndex]; + NSLog(@"Assigning to %@", currVariable); + variablesToValues[currVariable] = currValue; + } + currIndex++; + } + + //Second, check the conditionals to see + for (NSArray *currRule in self.conditionals) { + NSString *conditionalVariable = currRule[0]; + NSString *conditionalValue = currRule[1]; + NSString *targetVariable = currRule[2]; + NSString *targetValue = currRule[3]; + + if (!conditionalVariable || !targetVariable || !conditionalValue || !targetValue) { + continue; + } + + NSString *actualValue = variablesToValues[conditionalVariable]; + if (actualValue) { + if ([actualValue compare:conditionalValue options:NSCaseInsensitiveSearch] == NSOrderedSame) { + variablesToValues[targetVariable] = targetValue; + } + } + } + + NSLog(@"Var to Val: %@", variablesToValues); + NSMutableString *mutableCommand = [self.command mutableCopy]; + NSInteger offset = 0; + for (NSArray *currVariablePair in variableRanges) { + NSString *currVariable = currVariablePair[0]; + NSString *currValue = variablesToValues[currVariable]; + NSLog(@"Currently on %@ %@", currVariable, currValue); + if (!currVariable || !currValue) { + continue; + } + + NSRange rangeToReplace = [currVariablePair[1] rangeValue]; + rangeToReplace.location += offset; + + [mutableCommand replaceCharactersInRange:rangeToReplace withString:currValue]; + offset += currValue.length - rangeToReplace.length; + } + return mutableCommand; +} + +@end diff --git a/assistant+/assistantpluspluginmanager/APPluginSystem.h b/assistant+/assistantpluspluginmanager/APPluginSystem.h index 981b549..9925abe 100644 --- a/assistant+/assistantpluspluginmanager/APPluginSystem.h +++ b/assistant+/assistantpluspluginmanager/APPluginSystem.h @@ -13,6 +13,7 @@ @interface APPluginSystem : NSObject { NSMutableArray *plugins; NSMutableArray *activatorListenersArray; + NSMutableArray *captureGroupCommandsArray; } @property (strong, nonatomic) APSession *currSession; @@ -26,5 +27,8 @@ //1.0.1 - (void)siriSay:(NSString*)message; +//1.0.2 +- (void)reloadCaptureGroupCommands:(NSDictionary*)commands; + - (NSDictionary*)getInstalledPlugins; @end diff --git a/assistant+/assistantpluspluginmanager/APPluginSystem.m b/assistant+/assistantpluspluginmanager/APPluginSystem.m index 7720dba..9cbe0d8 100644 --- a/assistant+/assistantpluspluginmanager/APPluginSystem.m +++ b/assistant+/assistantpluspluginmanager/APPluginSystem.m @@ -9,6 +9,8 @@ #import "APPluginSystem.h" #import "APPlugin.h" #import "APActivatorListener.h" +#import "APCaptureGroupCommand.h" +#import "CPDistributedMessagingCenter.h" static NSString *PREFERENCE_PATH = @"/var/mobile/Library/Preferences/com.assistantplus.app.plist"; static NSString *EVENT_PREFIX = @"APListener"; @@ -28,6 +30,7 @@ static NSString *EVENT_PREFIX = @"APListener"; NSDictionary *pref = [NSDictionary dictionaryWithContentsOfFile:PREFERENCE_PATH]; [sharedManager reloadActivatorListeners:pref]; + [sharedManager reloadCaptureGroupCommands:pref]; }); return sharedManager; } @@ -58,9 +61,12 @@ static NSString *EVENT_PREFIX = @"APListener"; - (BOOL)handleCommand:(NSString*)command withTokens:(NSSet*)tokens withSession:(APSession*)currSession { self.currSession = currSession; - //First check activator listeners + + //Clean up the command NSString *userCommand = [command lowercaseString]; userCommand = [userCommand stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + //First check activator listeners for (APActivatorListener *currListener in activatorListenersArray) { for (NSRegularExpression *currExpression in currListener.triggers) { NSArray *arrayOfAllMatches = [currExpression matchesInString:userCommand options:0 range:NSMakeRange(0, [userCommand length])]; @@ -77,6 +83,27 @@ static NSString *EVENT_PREFIX = @"APListener"; } } + //Then check Capture Group Commands + for (APCaptureGroupCommand *currCommand in captureGroupCommandsArray) { + NSRegularExpression *currExpression = currCommand.trigger; + NSArray *arrayOfAllMatches = [currExpression matchesInString:userCommand options:0 range:NSMakeRange(0, [userCommand length])]; + for (NSTextCheckingResult *match in arrayOfAllMatches) { + if (match.numberOfRanges > 0) { + NSMutableArray *variableMatches = [[NSMutableArray alloc] init]; + for (NSInteger currIndex = 1; currIndex < match.numberOfRanges; currIndex++) { + NSString *variableValue = [[userCommand substringWithRange:[match rangeAtIndex:currIndex]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + NSLog(@"Match %ld: %@", (long)currIndex, variableValue); + [variableMatches addObject:variableValue]; + } + NSString *commandToExecute = [currCommand buildCommandWithValues:variableMatches]; + CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"]; + [center sendMessageName:@"runCommand" userInfo:@{@"command" : commandToExecute}]; + return YES; + } + } + } + + //Go through the plugins NSLog(@"AP: Got Command \"%@\"", userCommand); for (APPlugin *currPlugin in plugins) { if ([currPlugin handleSpeech:userCommand withTokens:tokens withSession:currSession]) { @@ -108,6 +135,22 @@ static NSString *EVENT_PREFIX = @"APListener"; } } +- (void)reloadCaptureGroupCommands:(NSDictionary*)commands { + NSLog(@"Loading capture group commands!"); + captureGroupCommandsArray = [[NSMutableArray alloc] init]; + + if ([commands objectForKey:@"captureGroupCommands"]) { + for (NSDictionary *currCommand in [commands objectForKey:@"captureGroupCommands"]) { + id triggerValue = currCommand[@"trigger"]; + id commandValue = currCommand[@"command"]; + if (triggerValue && commandValue) { + APCaptureGroupCommand *command = [[APCaptureGroupCommand alloc] initWithDictionary:currCommand]; + [captureGroupCommandsArray addObject:command]; + } + } + } +} + - (void)siriSay:(NSString*)message { [self.currSession sendTextSnippet:message temporary:NO scrollToTop:YES dialogPhase:@"Completion"]; } diff --git a/assistant+/assistantpluspluginmanager/APSpringboardUtils.h b/assistant+/assistantpluspluginmanager/APSpringboardUtils.h index 3b5acd8..f17f937 100644 --- a/assistant+/assistantpluspluginmanager/APSpringboardUtils.h +++ b/assistant+/assistantpluspluginmanager/APSpringboardUtils.h @@ -18,4 +18,5 @@ - (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *info))completion; - (void)loadPlugins; - (void)gotCurrentLocation:(NSString*)msg withInfo:(NSDictionary*)info; +- (void)runCommand:(NSString*)msg withInfo:(NSDictionary*)info; @end diff --git a/assistant+/assistantpluspluginmanager/APSpringboardUtils.m b/assistant+/assistantpluspluginmanager/APSpringboardUtils.m index 19c0dbd..3e10fa6 100644 --- a/assistant+/assistantpluspluginmanager/APSpringboardUtils.m +++ b/assistant+/assistantpluspluginmanager/APSpringboardUtils.m @@ -16,6 +16,12 @@ - (void)_relaunchSpringBoardNow; @end +@interface NSTask : NSObject +- (void)setLaunchPath:(NSString*)path; +- (void)setArguments:(NSArray*)args; +- (void)launch; +@end + @implementation APSpringboardUtils { APPluginSystem *pluginManager; @@ -33,9 +39,11 @@ [center registerForMessageName:@"RetrievedLocation" target:sharedObj selector:@selector(gotCurrentLocation:withInfo:)]; [center registerForMessageName:@"UpdateActivatorListeners" target:sharedObj selector:@selector(updateActivatorListeners:withListeners:)]; [center registerForMessageName:@"UpdateCustomReplies" target:sharedObj selector:@selector(updateCustomReplies:withReplies:)]; + [center registerForMessageName:@"UpdateCaptureGroupCommands" target:sharedObj selector:@selector(updateCaptureGroupCommands:withCommands:)]; [center registerForMessageName:@"respringForListeners" target:sharedObj selector:@selector(respring)]; [center registerForMessageName:@"getInstalledPlugins" target:sharedObj selector:@selector(getInstalledPlugins:withInfo:)]; [center registerForMessageName:@"siriSay" target:sharedObj selector:@selector(siriSay:withMessage:)]; + [center registerForMessageName:@"runCommand" target:sharedObj selector:@selector(runCommand:withInfo:)]; } } return sharedObj; @@ -54,6 +62,10 @@ [pluginManager reloadActivatorListeners:listeners]; } +- (void)updateCaptureGroupCommands:(NSString*)msg withCommands:(NSDictionary*)commands { + [pluginManager reloadCaptureGroupCommands:commands]; +} + - (void)updateCustomReplies:(NSString*)msg withReplies:(NSDictionary*)dict { [pluginManager reloadCustomRepliesPlugin:dict]; } @@ -92,6 +104,15 @@ [self stopLocationDaemon]; } +- (void)runCommand:(NSString*)msg withInfo:(NSDictionary*)info { + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath: @"/bin/sh"]; + NSArray *arguments = @[@"-c", + info[@"command"]]; + [task setArguments:arguments]; + [task launch]; +} + - (void)startLocationDaemon { system("/Applications/AssistantPlusApp.app/assistantplus_root_helper start"); } diff --git a/assistant+/assistantpluspluginmanager/AssistantPlusHeaders.h b/assistant+/assistantpluspluginmanager/AssistantPlusHeaders.h index 4408c08..8ad0a4f 100644 --- a/assistant+/assistantpluspluginmanager/AssistantPlusHeaders.h +++ b/assistant+/assistantpluspluginmanager/AssistantPlusHeaders.h @@ -24,6 +24,7 @@ @protocol APSharedUtils + (id)sharedAPUtils; - (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *info))completion; +- (void)runCommand:(NSString*)msg withInfo:(NSDictionary*)info; @end @protocol APPluginSystem diff --git a/assistant+/assistantpluspluginmanager/Makefile b/assistant+/assistantpluspluginmanager/Makefile index 12a42aa..4a15490 100644 --- a/assistant+/assistantpluspluginmanager/Makefile +++ b/assistant+/assistantpluspluginmanager/Makefile @@ -5,7 +5,7 @@ export TARGET_IPHONEOS_DEPLOYMENT_VERSION = 8.0 TWEAK_NAME = AssistantPlusPluginManager AssistantPlusPluginManager_CFLAGS = -fobjc-arc -AssistantPlusPluginManager_FILES = Tweak.xm APPluginSystem.m APPlugin.m APSpringboardUtils.m APActivatorListener.m +AssistantPlusPluginManager_FILES = Tweak.xm APPluginSystem.m APPlugin.m APSpringboardUtils.m APActivatorListener.m APCaptureGroupCommand.m AssistantPlusPluginManager_PRIVATE_FRAMEWORKS = AssistantServices SAObjects AppSupport AssistantPlusPluginManager_FRAMEWORKS = Foundation UIKit CoreLocation AssistantPlusPluginManager_LIBRARIES = substrate activator diff --git a/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/contents.xcworkspacedata b/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/contents.xcworkspacedata index 6d9b4f7..898eb2a 100644 --- a/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/contents.xcworkspacedata +++ b/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/contents.xcworkspacedata @@ -13,6 +13,12 @@ + + + + diff --git a/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate index 373ac43..8f631c5 100644 Binary files a/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate and b/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/assistant+/assistantpluspluginmanager/obj/AssistantPlusPluginManager.dylib b/assistant+/assistantpluspluginmanager/obj/AssistantPlusPluginManager.dylib index 3abb91a..1cb815e 100755 Binary files a/assistant+/assistantpluspluginmanager/obj/AssistantPlusPluginManager.dylib and b/assistant+/assistantpluspluginmanager/obj/AssistantPlusPluginManager.dylib differ diff --git a/assistant+/obj/Assistant+.dylib b/assistant+/obj/Assistant+.dylib index aa11214..8c29f18 100755 Binary files a/assistant+/obj/Assistant+.dylib and b/assistant+/obj/Assistant+.dylib differ diff --git a/spotifyPlugin/spotifyPlugin.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/spotifyPlugin/spotifyPlugin.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate index 6e9c349..38eafb3 100644 Binary files a/spotifyPlugin/spotifyPlugin.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate and b/spotifyPlugin/spotifyPlugin.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-1.0.0 b/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-1.0.0 index ca7bf83..a5c750f 100644 --- a/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-1.0.0 +++ b/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-1.0.0 @@ -1 +1 @@ -13 \ No newline at end of file +27 \ No newline at end of file diff --git a/spotifyPlugin/spotifyPlugin/Makefile b/spotifyPlugin/spotifyPlugin/Makefile index e86a617..2aaf704 100644 --- a/spotifyPlugin/spotifyPlugin/Makefile +++ b/spotifyPlugin/spotifyPlugin/Makefile @@ -1,6 +1,6 @@ include theos/makefiles/common.mk -export ARCHS = armv7 arm64 +export ARCHS = armv7 armv7s arm64 export TARGET = iphone:clang:latest:8.0 export SDKVERSION=8.1 diff --git a/spotifyPlugin/spotifyPlugin/_/DEBIAN/control b/spotifyPlugin/spotifyPlugin/_/DEBIAN/control index e998dc3..2123c9d 100644 --- a/spotifyPlugin/spotifyPlugin/_/DEBIAN/control +++ b/spotifyPlugin/spotifyPlugin/_/DEBIAN/control @@ -6,5 +6,5 @@ Description: Control Spotify using Siri! Currently spotifySiriControls supports Maintainer: Zaid Elkurdi Author: Zaid Elkurdi Section: Tweaks -Version: 1.0.0-13 -Installed-Size: 616 +Version: 1.0.0-27 +Installed-Size: 888 diff --git a/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/spotifySiri b/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/spotifySiri index b23b020..714f802 100755 Binary files a/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/spotifySiri and b/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/spotifySiri differ diff --git a/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/spotifySiri b/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/spotifySiri index b23b020..714f802 100755 Binary files a/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/spotifySiri and b/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/spotifySiri differ