diff --git a/assistant+/.theos/packages/com.zaid.assistant+-0.0.1 b/assistant+/.theos/packages/com.zaid.assistant+-0.0.1
deleted file mode 100644
index 0c7f592..0000000
--- a/assistant+/.theos/packages/com.zaid.assistant+-0.0.1
+++ /dev/null
@@ -1 +0,0 @@
-604
\ No newline at end of file
diff --git a/assistant+/.theos/packages/com.zaid.assistant+-1.0.0 b/assistant+/.theos/packages/com.zaid.assistant+-1.0.0
new file mode 100644
index 0000000..d8263ee
--- /dev/null
+++ b/assistant+/.theos/packages/com.zaid.assistant+-1.0.0
@@ -0,0 +1 @@
+2
\ No newline at end of file
diff --git a/assistant+/Assistant+.xcworkspace/contents.xcworkspacedata b/assistant+/Assistant+.xcworkspace/contents.xcworkspacedata
index a3da4df..a280aa1 100644
--- a/assistant+/Assistant+.xcworkspace/contents.xcworkspacedata
+++ b/assistant+/Assistant+.xcworkspace/contents.xcworkspacedata
@@ -14,4 +14,7 @@
+
+
diff --git a/assistant+/Assistant+.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/assistant+/Assistant+.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate
index 5f52197..45b6ddf 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+/AssistantHooks.xm b/assistant+/AssistantHooks.xm
index 307be98..e414db6 100644
--- a/assistant+/AssistantHooks.xm
+++ b/assistant+/AssistantHooks.xm
@@ -25,9 +25,11 @@ static BOOL hasLoadedSnippets = NO;
BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
pluginManager = [[%c(APSpringboardUtils) sharedAPUtils] getPluginManager];
- NSLog(@"Manager: %@", pluginManager);
- NSSet *tokens = [NSSet setWithArray:[text componentsSeparatedByString: @" "]];
- return [pluginManager handleCommand:text withTokens:tokens withSession:currSession];
+ NSArray *lowerCaseArr = [[text componentsSeparatedByString: @" "] valueForKey:@"lowercaseString"];
+ NSSet *tokens = [NSSet setWithArray:lowerCaseArr];
+ BOOL pluginWillHandle = [pluginManager handleCommand:text withTokens:tokens withSession:currSession];
+ defaultHandling = !pluginWillHandle;
+ return pluginWillHandle;
}
%hook BasicAceContext
@@ -43,13 +45,6 @@ BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
%end
-%hook SiriUISnippetViewController
--(void)setSnippet:(id)arg1 {
- %log;
- %orig;
-}
-%end
-
%hook SiriUIPluginManager
- (id)transcriptItemForObject:(AceObject*)arg1 {
@@ -60,11 +55,8 @@ BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
if (properties) {
NSString *className = properties[@"snippetClass"];
if (className) {
- NSLog(@"AP: Looking for custom snippet: %@", className);
- NSLog(@"Attempt to get: %@", NSClassFromString(className));
id customClass = [[NSClassFromString(className) alloc] initWithProperties:properties[@"snippetProps"]];
if ([customClass respondsToSelector:@selector(view)]) {
- NSLog(@"Custom class: %@", customClass);
UIViewController *customVC = (UIViewController*)customClass;
SiriUISnippetViewController *vc = [[%c(SiriUISnippetViewController) alloc] init];
object_setClass(vc, [%c(APPluginSnippetViewController) class]);
@@ -97,20 +89,18 @@ BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
- NSLog(@"SiriUIPluginManager: Plugins:");
for (NSURL *fileURL in contents) {
NSString *name = [[[fileURL absoluteString] lastPathComponent] stringByDeletingPathExtension];
- NSLog(@"Loading %@ at %@", name, fileURL);
NSBundle *bundle = [NSBundle bundleWithURL:fileURL];
if (!bundle) {
- NSLog(@"Failed to open extension bundle %@ (%@)!", fileURL, fileURL);
+ NSLog(@"Failed to open plugin bundle %@ (%@)!", fileURL, fileURL);
continue;
}
if (![bundle load]) {
- NSLog(@"Failed to load extension bundle %@ (wrong CFBundleExecutable? Missing? Not signed?)!", name);
+ NSLog(@"Failed to load plugin bundle %@!", name);
continue;
} else {
NSLog(@"Loaded bundle!");
@@ -121,14 +111,6 @@ BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
%end
%hook AFConnection
-- (void)_doCommand:(SAUIAddViews*)arg1 reply:(id)arg2 {
- NSLog(@"Doing: %@ with reply: %@", arg1, arg2);
- if ([arg1 respondsToSelector:@selector(views)]) {
- NSLog(@"Views: %@", arg1.views);
- }
- %log;
- %orig;
-}
- (void)startRequestWithCorrectedText:(NSString*)text forSpeechIdentifier:(id)arg2 {
NSLog(@"AP: Starting request with corrected text: %@", text);
@@ -140,17 +122,6 @@ BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
}
}
-- (void)startAcousticIDRequestWithOptions:(id)arg1 { %log; %orig; }
-- (void)startSpeechPronunciationRequestWithOptions:(id)arg1 pronunciationContext:(id)arg2 { %log; %orig; }
-
-- (void)startSpeechRequestWithOptions:(id)arg1 {
- NSLog(@"%@", arg1);
- %log;
- %orig;
-}
-- (void)startContinuationRequestWithUserInfo:(id)arg1 { %log; %orig; }
-- (void)startDirectActionRequestWithString:(id)arg1 { %log; %orig; }
-
- (void)startRequestWithText:(NSString*)text {
NSLog(@"AP: Starting request with text: %@", text);
APSession *currSession = [APSession sessionWithConnection:self];
@@ -164,28 +135,6 @@ BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
%end
-%hook AFUISiriSession
-- (void)_requestContextWithCompletion:(id)arg1 {
- NSLog(@"%@", arg1);
- %log;
- %orig;
-}
-- (void)_requestDidFinishWithError:(id)arg1 {
- NSLog(@"%@", arg1);
- %log;
- %orig;
-}
-- (void)assistantConnectionRequestFinished:(id)arg1 {
- NSLog(@"%@", arg1);
- %log;
- %orig;
-}
-- (void)end {
- %log;
- %orig;
-}
-%end
-
%hook AFConnectionClientServiceDelegate
- (void)speechRecognized:(SASSpeechRecognized*)arg1 {
@@ -194,8 +143,9 @@ BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
if (currPhrase.interpretations.count > 0) {
SASInterpretation *currInterpretation = currPhrase.interpretations[0];
if (currInterpretation.tokens.count > 0) {
- AFSpeechToken *currToken = currInterpretation.tokens[0];
- [phraseBuilder appendString:[NSString stringWithFormat:@"%@ ", currToken.text]];
+ for (AFSpeechToken *currToken in currInterpretation.tokens) {
+ [phraseBuilder appendString:[NSString stringWithFormat:@"%@ ", currToken.text]];
+ }
}
}
}
@@ -203,23 +153,21 @@ BOOL shouldHandleRequest(NSString *text, APSession *currSession) {
NSLog(@"AP Starting Speech Query: %@", phraseBuilder);
AFConnection *connection = MSHookIvar(self, "_connection");
-
APSession *currSession = [APSession sessionWithConnection:connection];
if (shouldHandleRequest(phraseBuilder, currSession)) {
- defaultHandling = NO;
- NSLog(@"Handling with plugin!");
+ [connection cancelRequest];
+ [self requestDidFinish];
} else {
- defaultHandling = YES;
- NSLog(@"Going to default!");
%orig;
}
}
-- (void)requestDidFinish{ %log; %orig; }
- (void)requestDidReceiveCommand:(id)arg1 reply:(CDUnknownBlockType*)arg2 {
- %log;
if (defaultHandling) {
+ NSLog(@"Allowing default!");
%orig;
+ } else {
+ NSLog(@"Overriding!");
}
}
%end
\ No newline at end of file
diff --git a/assistant+/Makefile b/assistant+/Makefile
index 31794e8..4509dc2 100644
--- a/assistant+/Makefile
+++ b/assistant+/Makefile
@@ -2,7 +2,8 @@ include theos/makefiles/common.mk
export ARCHS = armv7s arm64
export TARGET = iphone:clang:latest:8.0
-export SDKVERSION=8.2
+export TARGET_IPHONEOS_DEPLOYMENT_VERSION = 8.0
+export SDKVERSION=8.1
TWEAK_NAME = Assistant+
@@ -15,11 +16,16 @@ Assistant+_CFLAGS = -fobjc-arc
include $(THEOS_MAKE_PATH)/tweak.mk
+after-stage::
+ chmod u+s $(THEOS_STAGING_DIR)/Applications/AssistantPlusApp.app/assistantplus_root_helper
+
after-install::
install.exec "killall -9 SpringBoard"
SUBPROJECTS += assistantpluspluginmanager
SUBPROJECTS += aplocationdaemon
SUBPROJECTS += assistantplusapp
+SUBPROJECTS += assistantplus_root_helper
+SUBPROJECTS += customreply
include $(THEOS_MAKE_PATH)/aggregate.mk
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/AssistantPlusApp b/assistant+/_/Applications/AssistantPlusApp.app/AssistantPlusApp
index 670e887..bb7021a 100755
Binary files a/assistant+/_/Applications/AssistantPlusApp.app/AssistantPlusApp and b/assistant+/_/Applications/AssistantPlusApp.app/AssistantPlusApp differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default-568h@2x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default-568h@2x.png
index beb6512..5985e80 100644
Binary files a/assistant+/_/Applications/AssistantPlusApp.app/Default-568h@2x.png and b/assistant+/_/Applications/AssistantPlusApp.app/Default-568h@2x.png differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default-667h@2x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default-667h@2x.png
deleted file mode 100644
index b27e252..0000000
Binary files a/assistant+/_/Applications/AssistantPlusApp.app/Default-667h@2x.png and /dev/null differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default-700-568h@2x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default-700-568h@2x.png
new file mode 100755
index 0000000..0f5aa5f
Binary files /dev/null and b/assistant+/_/Applications/AssistantPlusApp.app/Default-700-568h@2x.png differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default-700@2x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default-700@2x.png
new file mode 100755
index 0000000..42a3e34
Binary files /dev/null and b/assistant+/_/Applications/AssistantPlusApp.app/Default-700@2x.png differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default-736h@3x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default-736h@3x.png
deleted file mode 100644
index adce1f8..0000000
Binary files a/assistant+/_/Applications/AssistantPlusApp.app/Default-736h@3x.png and /dev/null differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default-800-667h@2x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default-800-667h@2x.png
new file mode 100755
index 0000000..bdf275c
Binary files /dev/null and b/assistant+/_/Applications/AssistantPlusApp.app/Default-800-667h@2x.png differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default-800-667h@3x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default-800-667h@3x.png
new file mode 100755
index 0000000..6602ea2
Binary files /dev/null and b/assistant+/_/Applications/AssistantPlusApp.app/Default-800-667h@3x.png differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default-800-Portrait-736h@3x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default-800-Portrait-736h@3x.png
new file mode 100755
index 0000000..5591fc3
Binary files /dev/null and b/assistant+/_/Applications/AssistantPlusApp.app/Default-800-Portrait-736h@3x.png differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Default@2x.png b/assistant+/_/Applications/AssistantPlusApp.app/Default@2x.png
deleted file mode 100644
index e56f94e..0000000
Binary files a/assistant+/_/Applications/AssistantPlusApp.app/Default@2x.png and /dev/null differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Icon.png b/assistant+/_/Applications/AssistantPlusApp.app/Icon.png
new file mode 100644
index 0000000..a07e3f1
Binary files /dev/null and b/assistant+/_/Applications/AssistantPlusApp.app/Icon.png differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/Info.plist b/assistant+/_/Applications/AssistantPlusApp.app/Info.plist
index c39ecb6..71021ea 100644
--- a/assistant+/_/Applications/AssistantPlusApp.app/Info.plist
+++ b/assistant+/_/Applications/AssistantPlusApp.app/Info.plist
@@ -4,8 +4,6 @@
CFBundleDisplayName
Assistant+
- UIStatusBarStyle
- UIStatusBarStyleDefault
CFBundleDevelopmentRegion
en
CFBundleSupportedPlatforms
@@ -23,7 +21,7 @@
1
LSRequiresIPhoneOS
-
+
CFBundlePackageType
APPL
CFBundleShortVersionString
@@ -34,27 +32,25 @@
8.0
CFBundleVersion
1
- UIStatusBarTintParameters
-
- UINavigationBar
-
- Style
- UIBarStyleDefault
- Translucent
- false
-
-
- UIStatusBarHidden
-
- UILaunchImageFile
- Default.png
+ CFBundleIconFile
+ Icon.png
UILaunchImages
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default
+ Default-568h
+ UILaunchImageOrientation
+ Portrait
+ UILaunchImageSize
+ {640, 1136}
+
+
+ UILaunchImageMinimumOSVersion
+ 7.0
+ UILaunchImageName
+ Default-700
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -62,19 +58,9 @@
UILaunchImageMinimumOSVersion
- 8.0
+ 7.0
UILaunchImageName
- Default
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {320, 480}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-568h
+ Default-700-568h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -84,17 +70,7 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-568h
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {320, 568}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-667h
+ Default-800-667h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -104,17 +80,7 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-667h
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {375, 667}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-736h
+ Default-800-Portrait-736h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -124,35 +90,13 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-736h
+ Default-800-Landscape-736h
UILaunchImageOrientation
Landscape
UILaunchImageSize
{414, 736}
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-Portrait
- UILaunchImageOrientation
- Portrait
- UILaunchImageSize
- {768, 1024}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-Landscape
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {768, 1024}
-
- CFBundleIconFile
- Icon.png
CFBundleIconFiles
Icon.png
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/LaunchScreen.nib b/assistant+/_/Applications/AssistantPlusApp.app/LaunchScreen.nib
deleted file mode 100644
index 1d22d8b..0000000
Binary files a/assistant+/_/Applications/AssistantPlusApp.app/LaunchScreen.nib and /dev/null differ
diff --git a/assistant+/_/Applications/AssistantPlusApp.app/assistantplus_root_helper b/assistant+/_/Applications/AssistantPlusApp.app/assistantplus_root_helper
new file mode 100755
index 0000000..511d7df
Binary files /dev/null and b/assistant+/_/Applications/AssistantPlusApp.app/assistantplus_root_helper differ
diff --git a/assistant+/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/Info.plist b/assistant+/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/Info.plist
new file mode 100644
index 0000000..0e1a75f
--- /dev/null
+++ b/assistant+/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/Info.plist
@@ -0,0 +1,41 @@
+
+
+
+
+ CFBundleName
+ CustomReply
+ APPluginName
+ Custom Replies
+ APPluginAuthor
+ Zaid Elkurdi
+ CFBundleIdentifier
+ com.assistantplus.customreplyidentifier
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleVersion
+ 1
+ CFBundleDisplayName
+ CustomReply
+ MinimumOSVersion
+ 5.0
+ CFBundleSupportedPlatforms
+
+ iPhoneOS
+
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ AppBundleID
+ com.assistantplus.customreplyidentifier
+ UIDeviceFamily
+
+ 1
+ 2
+
+ CFBundleShortVersionString
+ 1.0
+ NSPrincipalClass
+ customreply
+
+
diff --git a/assistant+/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/customreply b/assistant+/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/customreply
new file mode 100755
index 0000000..dbfeed5
Binary files /dev/null and b/assistant+/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/customreply differ
diff --git a/assistant+/_/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist b/assistant+/_/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
index a69e1c6..fc36cb5 100644
--- a/assistant+/_/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
+++ b/assistant+/_/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
@@ -2,8 +2,6 @@
- KeepAlive
-
StandardErrorPath
/dev/null
RunAtLoad
diff --git a/assistant+/_/Library/MobileSubstrate/DynamicLibraries/Assistant+.dylib b/assistant+/_/Library/MobileSubstrate/DynamicLibraries/Assistant+.dylib
index 4cf52a6..791fe6d 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 698a695..fe14b22 100755
Binary files a/assistant+/_/Library/MobileSubstrate/DynamicLibraries/AssistantPlusPluginManager.dylib and b/assistant+/_/Library/MobileSubstrate/DynamicLibraries/AssistantPlusPluginManager.dylib differ
diff --git a/assistant+/_/debian/control b/assistant+/_/debian/control
index 3ecb943..0920794 100644
--- a/assistant+/_/debian/control
+++ b/assistant+/_/debian/control
@@ -1,10 +1,11 @@
Package: com.zaid.assistant+
Name: Assistant+
-Depends: mobilesubstrate
+Depends: libactivator (>= 1.8.3), mobilesubstrate
Architecture: iphoneos-arm
-Description: Framework for Siri Extensions
+Description: Assign commands to trigger Activator events from Siri, set up custom replies for Siri, and also use and create plugins for Siri using the Assistant+ plugin framework
Maintainer: Zaid Elkurdi
Author: Zaid Elkurdi
Section: Tweaks
-Version: 0.0.1-604
-Installed-Size: 792
+Icon: file:///Applications/AssistantPlusApp.app/Icon.png
+Version: 1.0.0-2
+Installed-Size: 1120
diff --git a/assistant+/_/debian/postinst b/assistant+/_/debian/postinst
index 4f067be..03d7c60 100755
--- a/assistant+/_/debian/postinst
+++ b/assistant+/_/debian/postinst
@@ -1,6 +1,11 @@
#!/bin/sh
chown root:wheel /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
-launchctl unload /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
+launchctl unload /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist 2> /dev/null
launchctl load /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
-echo "Starting APLocationManger..."
-exit 0
+
+chown root:wheel /Applications/AssistantPlusApp.app/assistantplus_root_helper
+chmod 6755 /Applications/AssistantPlusApp.app/assistantplus_root_helper
+
+uicache
+
+exit 0
\ No newline at end of file
diff --git a/assistant+/_/debian/preinst b/assistant+/_/debian/preinst
deleted file mode 100755
index 97b91c0..0000000
--- a/assistant+/_/debian/preinst
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-echo "Installing APLocationManager!"
-chown root:wheel /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
-launchctl unload /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
-exit 0
diff --git a/assistant+/_/usr/bin/APLocationDaemon b/assistant+/_/usr/bin/APLocationDaemon
index fe2fa84..dfb6d0e 100755
Binary files a/assistant+/_/usr/bin/APLocationDaemon and b/assistant+/_/usr/bin/APLocationDaemon differ
diff --git a/assistant+/aplocationdaemon/APLocationDaemon.xcworkspace/contents.xcworkspacedata b/assistant+/aplocationdaemon/APLocationDaemon.xcworkspace/contents.xcworkspacedata
index fc4cc80..0d5c385 100644
--- a/assistant+/aplocationdaemon/APLocationDaemon.xcworkspace/contents.xcworkspacedata
+++ b/assistant+/aplocationdaemon/APLocationDaemon.xcworkspace/contents.xcworkspacedata
@@ -19,9 +19,6 @@
-
-
diff --git a/assistant+/aplocationdaemon/APLocationDaemon.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/assistant+/aplocationdaemon/APLocationDaemon.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate
index a2ef923..e58f325 100644
Binary files a/assistant+/aplocationdaemon/APLocationDaemon.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate and b/assistant+/aplocationdaemon/APLocationDaemon.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/assistant+/aplocationdaemon/APLocationManager.h b/assistant+/aplocationdaemon/APLocationManager.h
index 9690252..3ffea17 100644
--- a/assistant+/aplocationdaemon/APLocationManager.h
+++ b/assistant+/aplocationdaemon/APLocationManager.h
@@ -9,7 +9,6 @@
#import "CPDistributedMessagingCenter.h"
@interface APLocationManager : NSObject
-@property (nonatomic) BOOL shouldTerminate;
- (void)startMonitoringLocation;
- (void)stopMonitoringLocation;
diff --git a/assistant+/aplocationdaemon/APLocationManager.m b/assistant+/aplocationdaemon/APLocationManager.m
index c4c2722..20bd22f 100644
--- a/assistant+/aplocationdaemon/APLocationManager.m
+++ b/assistant+/aplocationdaemon/APLocationManager.m
@@ -22,20 +22,16 @@
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
- self.shouldTerminate = NO;
}
return self;
}
- (void)startMonitoringLocation {
- NSLog(@"Start monitoring!");
[locationManager startUpdatingLocation];
}
- (void)stopMonitoringLocation {
- NSLog(@"Stop monitoring!");
[locationManager stopUpdatingLocation];
-
}
#pragma mark - CLLocationManagerDelegate
@@ -54,7 +50,6 @@
@"timestamp" : currLocation.timestamp};
CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"];
- NSLog(@"Sending %@ to APSpringboardUtils", dict);
[center sendMessageName:@"RetrievedLocation" userInfo:@{@"Location" : dict}];
}
diff --git a/assistant+/aplocationdaemon/Makefile b/assistant+/aplocationdaemon/Makefile
index 21cb3a0..425f393 100644
--- a/assistant+/aplocationdaemon/Makefile
+++ b/assistant+/aplocationdaemon/Makefile
@@ -14,7 +14,4 @@ APLocationDaemon_PRIVATE_FRAMEWORKS = AppSupport
APLocationDaemon_LIBRARIES = substrate
APLocationDaemon_CODESIGN_FLAGS = -Sentitlements.xml
-include $(THEOS_MAKE_PATH)/tool.mk
-
-after-APLocationDaemon-stage::
- $(ECHO_NOTHING)$(FAKEROOT) chown root:wheel $(THEOS_STAGING_DIR)/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist$(ECHO_END)
\ No newline at end of file
+include $(THEOS_MAKE_PATH)/tool.mk
\ No newline at end of file
diff --git a/assistant+/aplocationdaemon/layout/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist b/assistant+/aplocationdaemon/layout/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
index a69e1c6..fc36cb5 100644
--- a/assistant+/aplocationdaemon/layout/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
+++ b/assistant+/aplocationdaemon/layout/Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
@@ -2,8 +2,6 @@
- KeepAlive
-
StandardErrorPath
/dev/null
RunAtLoad
diff --git a/assistant+/aplocationdaemon/main.mm b/assistant+/aplocationdaemon/main.mm
index 3961644..fb451fc 100644
--- a/assistant+/aplocationdaemon/main.mm
+++ b/assistant+/aplocationdaemon/main.mm
@@ -13,6 +13,7 @@ int main(int argc, char *argv[]) {
NSLog(@"Starting this shit 2");
APLocationManager *manager = [[APLocationManager alloc] init];
+ [manager startMonitoringLocation];
CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.daemon"];
[center runServerOnCurrentThread];
[center registerForMessageName:@"RetrieveLocation" target:manager selector:@selector(startMonitoringLocation)];
diff --git a/assistant+/aplocationdaemon/obj/APLocationDaemon b/assistant+/aplocationdaemon/obj/APLocationDaemon
index fe2fa84..dfb6d0e 100755
Binary files a/assistant+/aplocationdaemon/obj/APLocationDaemon and b/assistant+/aplocationdaemon/obj/APLocationDaemon differ
diff --git a/assistant+/assistantplus_root_helper/Makefile b/assistant+/assistantplus_root_helper/Makefile
new file mode 100755
index 0000000..9b89e2f
--- /dev/null
+++ b/assistant+/assistantplus_root_helper/Makefile
@@ -0,0 +1,8 @@
+export GO_EASY_ON_ME=1
+
+TOOL_NAME = assistantplus_root_helper
+assistantplus_root_helper_INSTALL_PATH = /Applications/AssistantPlusApp.app
+assistantplus_root_helper_OBJC_FILES = assistantplus_root_helper.c
+
+include $(THEOS)/makefiles/common.mk
+include $(THEOS)/makefiles/tool.mk
diff --git a/assistant+/assistantplus_root_helper/assistantplus_root_helper.c b/assistant+/assistantplus_root_helper/assistantplus_root_helper.c
new file mode 100755
index 0000000..1b46df2
--- /dev/null
+++ b/assistant+/assistantplus_root_helper/assistantplus_root_helper.c
@@ -0,0 +1,34 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "paths.h"
+
+void startLocationDaemon() {
+ system("/bin/launchctl load /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist");
+}
+
+
+void stopLocationDaemon() {
+ system("/bin/launchctl unload /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist");
+}
+
+int main(int argc, const char *argv[]) {
+ // Run as root.
+ if (setuid(0) != 0) {
+ fprintf(stderr, "setuid failed. Error: %d.\n", errno);
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(argv[1], "start") == 0) {
+ startLocationDaemon();
+ } else if (strcmp(argv[1], "stop") == 0) {
+ stopLocationDaemon();
+ }
+ return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.pbxproj b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..eb04687
--- /dev/null
+++ b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.pbxproj
@@ -0,0 +1,138 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXFileReference section */
+ B86E66E11ACE4084007C6014 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; };
+ B86E66E21ACE4084007C6014 /* assistantplus_root_helper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = assistantplus_root_helper.c; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXGroup section */
+ B86E66DB1ACE4084007C6014 = {
+ isa = PBXGroup;
+ children = (
+ B86E66E11ACE4084007C6014 /* Makefile */,
+ B86E66E21ACE4084007C6014 /* assistantplus_root_helper.c */,
+ );
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXLegacyTarget section */
+ B86E66E01ACE4084007C6014 /* assistantplus_root_helper */ = {
+ isa = PBXLegacyTarget;
+ buildArgumentsString = "$(ACTION)";
+ buildConfigurationList = B86E66E31ACE4084007C6014 /* Build configuration list for PBXLegacyTarget "assistantplus_root_helper" */;
+ buildPhases = (
+ );
+ buildToolPath = /usr/bin/make;
+ buildWorkingDirectory = "/Users/Zaid/Programming/Assistant2/assistant+/assistantplus_root_helper";
+ dependencies = (
+ );
+ name = assistantplus_root_helper;
+ passBuildSettingsInEnvironment = 1;
+ productName = assistantplus_root_helper;
+ };
+/* End PBXLegacyTarget section */
+
+/* Begin PBXProject section */
+ B86E66DC1ACE4084007C6014 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ };
+ buildConfigurationList = B86E66DF1ACE4084007C6014 /* Build configuration list for PBXProject "assistantplus_root_helper" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = B86E66DB1ACE4084007C6014;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ B86E66E01ACE4084007C6014 /* assistantplus_root_helper */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin XCBuildConfiguration section */
+ B86E66DD1ACE4084007C6014 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ COPY_PHASE_STRIP = NO;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx10.6;
+ };
+ name = Debug;
+ };
+ B86E66DE1ACE4084007C6014 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ COPY_PHASE_STRIP = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ SDKROOT = macosx10.6;
+ };
+ name = Release;
+ };
+ B86E66E41ACE4084007C6014 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ DEBUGGING_SYMBOLS = YES;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = assistantplus_root_helper;
+ };
+ name = Debug;
+ };
+ B86E66E51ACE4084007C6014 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = assistantplus_root_helper;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ B86E66DF1ACE4084007C6014 /* Build configuration list for PBXProject "assistantplus_root_helper" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B86E66DD1ACE4084007C6014 /* Debug */,
+ B86E66DE1ACE4084007C6014 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ B86E66E31ACE4084007C6014 /* Build configuration list for PBXLegacyTarget "assistantplus_root_helper" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B86E66E41ACE4084007C6014 /* Debug */,
+ B86E66E51ACE4084007C6014 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = B86E66DC1ACE4084007C6014 /* Project object */;
+}
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
new file mode 100644
index 0000000..94b2795
--- /dev/null
+++ b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,4 @@
+
+
+
diff --git a/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/assistantplus_root_helper.xcscheme b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/assistantplus_root_helper.xcscheme
new file mode 100644
index 0000000..a20d564
--- /dev/null
+++ b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/assistantplus_root_helper.xcscheme
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/xcschememanagement.plist b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..2631f80
--- /dev/null
+++ b/assistant+/assistantplus_root_helper/assistantplus_root_helper.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ SchemeUserState
+
+ assistantplus_root_helper.xcscheme
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ B86E66E01ACE4084007C6014
+
+ primary
+
+
+
+
+
diff --git a/assistant+/assistantplus_root_helper/obj/.stamp b/assistant+/assistantplus_root_helper/obj/.stamp
new file mode 100644
index 0000000..e69de29
diff --git a/assistant+/assistantplus_root_helper/obj/assistantplus_root_helper b/assistant+/assistantplus_root_helper/obj/assistantplus_root_helper
new file mode 100755
index 0000000..511d7df
Binary files /dev/null and b/assistant+/assistantplus_root_helper/obj/assistantplus_root_helper differ
diff --git a/assistant+/assistantplusapp/ActivatorListenersViewController.h b/assistant+/assistantplusapp/ActivatorListenersViewController.h
index 03a7377..f548766 100644
--- a/assistant+/assistantplusapp/ActivatorListenersViewController.h
+++ b/assistant+/assistantplusapp/ActivatorListenersViewController.h
@@ -8,7 +8,8 @@
#import
#import
+#import "ListenerDetailViewController.h"
-@interface ActivatorListenersViewController : UIViewController
+@interface ActivatorListenersViewController : UIViewController
@property (strong, nonatomic) UITableView *listenersTable;
@end
\ No newline at end of file
diff --git a/assistant+/assistantplusapp/ActivatorListenersViewController.m b/assistant+/assistantplusapp/ActivatorListenersViewController.m
index 4e55a06..a189bb5 100644
--- a/assistant+/assistantplusapp/ActivatorListenersViewController.m
+++ b/assistant+/assistantplusapp/ActivatorListenersViewController.m
@@ -8,7 +8,6 @@
#import "ActivatorListenersViewController.h"
#import "APActivatorListener.h"
-#import "ListenerDetailViewController.h"
#import "CPDistributedMessagingCenter.h"
@interface ActivatorListenersViewController ()
@@ -22,6 +21,8 @@
- (void)viewDidLoad {
[super viewDidLoad];
+ self.title = @"Listeners";
+
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewListener:)];
[self.navigationItem setRightBarButtonItem:addButton];
@@ -29,21 +30,21 @@
self.listenersTable.delegate = self;
self.listenersTable.dataSource = self;
- UIView *msgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 130)];
- UILabel *msgLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 0, self.view.frame.size.width-40, 60)];
+ UIView *msgView = [[UIView alloc] initWithFrame:CGRectMake(0, 10, self.view.frame.size.width, 130)];
+ UILabel *msgLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, 60)];
msgLabel.lineBreakMode = NSLineBreakByWordWrapping;
- msgLabel.numberOfLines = 2;
- msgLabel.text = @"You must respring your device before new listeners will appear in Activator";
+ msgLabel.numberOfLines = 0;
+ msgLabel.text = @"You must respring your device before listeners you delete here will be removed from Activator";
msgLabel.textAlignment = NSTextAlignmentCenter;
msgLabel.font = [UIFont fontWithName:@"Helvetica" size:16];
msgLabel.textColor = [UIColor darkGrayColor];
[msgView addSubview:msgLabel];
UIButton *respringButton = [UIButton buttonWithType:UIButtonTypeSystem];
- respringButton.frame = CGRectMake(0, 60, self.view.frame.size.width, 40);
+ respringButton.frame = CGRectMake(0, 70, self.view.frame.size.width, 40);
[respringButton addTarget:self action:@selector(respringPressed:) forControlEvents:UIControlEventTouchUpInside];
[respringButton setTitle:@"Respring" forState:UIControlStateNormal];
- respringButton.titleLabel.font = [UIFont systemFontOfSize:20];
+ respringButton.titleLabel.font = [UIFont systemFontOfSize:18];
[msgView addSubview:respringButton];
self.listenersTable.tableFooterView = msgView;
@@ -62,9 +63,6 @@
if ([self.listenersTable indexPathForSelectedRow]) {
[self.listenersTable deselectRowAtIndexPath:[self.listenersTable indexPathForSelectedRow] animated:YES];
}
-
- [self saveListenersToFile];
- [self.listenersTable reloadData];
}
- (void)didReceiveMemoryWarning {
@@ -76,18 +74,18 @@
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:@"activatorListeners"]) {
NSArray *listeners = [defaults objectForKey:@"activatorListeners"];
- NSLog(@"Serialized listeners: %@", listeners);
for (NSDictionary *currListener in listeners) {
APActivatorListener *listener = [[APActivatorListener alloc] initWithDictionary:currListener];
[savedListeners addObject:listener];
}
}
+
}
- (void)saveListenersToFile {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSMutableArray *toSave = [[NSMutableArray alloc] init];
if (savedListeners) {
- NSMutableArray *toSave = [[NSMutableArray alloc] init];
for (APActivatorListener *currListener in savedListeners) {
[toSave addObject:[currListener dictionaryRepresentation]];
}
@@ -96,7 +94,7 @@
}
CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"];
- [center sendMessageName:@"UpdateActivatorListeners" userInfo:nil];
+ [center sendMessageName:@"UpdateActivatorListeners" userInfo:@{@"activatorListeners" : toSave}];
}
#pragma mark - Button Handlers
@@ -119,7 +117,6 @@
}
- (void)respringPressed:(UIButton*)button {
- NSLog(@"Will respring!");
CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"];
[center sendMessageName:@"respringForListeners" userInfo:nil];
}
@@ -145,6 +142,7 @@
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
APActivatorListener *selectedListener = [savedListeners objectAtIndex:indexPath.row];
ListenerDetailViewController *detailVC = [[ListenerDetailViewController alloc] initWithListener:selectedListener];
+ detailVC.delegate = self;
[self.navigationController pushViewController:detailVC animated:YES];
}
@@ -169,4 +167,20 @@
return 60;
}
+#pragma mark - ListenerDelegate
+
+- (void)listenerDidChange:(APActivatorListener *)listener {
+ for (NSInteger currIndex = 0; currIndex < savedListeners.count; currIndex++) {
+ APActivatorListener *currListener = [savedListeners objectAtIndex:currIndex];
+ if ([currListener.uniqueId isEqualToString:currListener.uniqueId]) {
+ [savedListeners replaceObjectAtIndex:currIndex withObject:currListener];
+ break;
+ }
+ }
+
+ [self saveListenersToFile];
+ [self.listenersTable reloadData];
+}
+
+
@end
diff --git a/assistant+/assistantplusapp/AppDelegate.h b/assistant+/assistantplusapp/AppDelegate.h
index 4317c28..34f5494 100644
--- a/assistant+/assistantplusapp/AppDelegate.h
+++ b/assistant+/assistantplusapp/AppDelegate.h
@@ -7,10 +7,12 @@
//
#import
+#import "MainViewController.h"
@interface AppDelegate : UIResponder
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navController;
+@property (strong, nonatomic) MainViewController *mainController;
@end
diff --git a/assistant+/assistantplusapp/AppDelegate.m b/assistant+/assistantplusapp/AppDelegate.m
index 8d071b6..e7a16fa 100644
--- a/assistant+/assistantplusapp/AppDelegate.m
+++ b/assistant+/assistantplusapp/AppDelegate.m
@@ -1,4 +1,3 @@
-#import "MainViewController.h"
#import "AppDelegate.h"
@implementation AppDelegate
@@ -7,9 +6,9 @@
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
- MainViewController *mainVC = [[MainViewController alloc] init];
+ self.mainController = [[MainViewController alloc] init];
- self.navController = [[UINavigationController alloc] initWithRootViewController:mainVC];
+ self.navController = [[UINavigationController alloc] initWithRootViewController:self.mainController];
self.navController.title = @"Assistant+";
self.navController.view.backgroundColor = [UIColor whiteColor];
[self.window setRootViewController:self.navController];
@@ -17,14 +16,6 @@
return YES;
}
-- (void)applicationDidBecomeActive:(UIApplication *)application {
- [[UIApplication sharedApplication] setStatusBarHidden:NO];
-}
-
-- (void)applicationDidEnterBackground:(UIApplication *)application {
- [[UIApplication sharedApplication] setStatusBarHidden:YES];
-}
-
@end
// vim:ft=objc
\ No newline at end of file
diff --git a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.pbxproj b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.pbxproj
index dd6e63f..61483ec 100644
--- a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.pbxproj
+++ b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/project.pbxproj
@@ -8,6 +8,18 @@
/* Begin PBXBuildFile section */
B834F0171AC5FEEF00CF009E /* AssistantPlusAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B834F0161AC5FEEF00CF009E /* AssistantPlusAppTests.m */; };
+ B86E66DA1ACD1991007C6014 /* PluginsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B86E66D91ACD1991007C6014 /* PluginsViewController.m */; };
+ B86E67101ACE7AC0007C6014 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0A7C1ABF79AA00D4D107 /* AppDelegate.m */; };
+ B86E67111ACE7AC0007C6014 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0A7F1ABF79AA00D4D107 /* MainViewController.m */; };
+ B86E67121ACE7AC0007C6014 /* PluginsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B86E66D91ACD1991007C6014 /* PluginsViewController.m */; };
+ B86E67131ACE7AC0007C6014 /* CustomRepliesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0ADF1AC32C2000D4D107 /* CustomRepliesViewController.m */; };
+ B86E67141ACE7AC0007C6014 /* CustomReplyDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0AE21AC32C2A00D4D107 /* CustomReplyDetailViewController.m */; };
+ B86E67151ACE7AC0007C6014 /* ActivatorListenersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0ACD1ABF8F7C00D4D107 /* ActivatorListenersViewController.m */; };
+ B86E67161ACE7AC0007C6014 /* ListenerDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0AD91AC0949D00D4D107 /* ListenerDetailViewController.m */; };
+ B86E67171ACE7AC0007C6014 /* APActivatorListener.m in Sources */ = {isa = PBXBuildFile; fileRef = B88C0AD21ABF968B00D4D107 /* APActivatorListener.m */; };
+ 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 */; };
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 */; };
@@ -27,6 +39,13 @@
remoteGlobalIDString = B834EFF71AC5FEEF00CF009E;
remoteInfo = AssistantPlusApp;
};
+ B86E67031ACE7A77007C6014 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B88C0A561ABF794E00D4D107 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = B86E66E91ACE7A77007C6014;
+ remoteInfo = assistantapp;
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@@ -37,6 +56,11 @@
B834F0101AC5FEEF00CF009E /* AssistantPlusAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AssistantPlusAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B834F0151AC5FEEF00CF009E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
B834F0161AC5FEEF00CF009E /* AssistantPlusAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AssistantPlusAppTests.m; sourceTree = ""; };
+ B86E66D81ACD1991007C6014 /* PluginsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginsViewController.h; sourceTree = SOURCE_ROOT; };
+ B86E66D91ACD1991007C6014 /* PluginsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PluginsViewController.m; sourceTree = SOURCE_ROOT; };
+ B86E66EA1ACE7A77007C6014 /* assistantapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = assistantapp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ B86E67021ACE7A77007C6014 /* assistantappTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = assistantappTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ B86E671A1ACE7C66007C6014 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "Resources/Default-568h@2x.png"; sourceTree = ""; };
B88C0A7C1ABF79AA00D4D107 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
B88C0A7E1ABF79AA00D4D107 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = SOURCE_ROOT; };
B88C0A7F1ABF79AA00D4D107 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = SOURCE_ROOT; };
@@ -75,6 +99,20 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ B86E66E71ACE7A77007C6014 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ B86E66FF1ACE7A77007C6014 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -107,6 +145,7 @@
B88C0A551ABF794E00D4D107 = {
isa = PBXGroup;
children = (
+ B86E671A1ACE7C66007C6014 /* Default-568h@2x.png */,
B88C0A601ABF794E00D4D107 /* AssistantPlusApp */,
B834F0131AC5FEEF00CF009E /* AssistantPlusAppTests */,
B834EFB81AC51E4600CF009E /* Frameworks */,
@@ -120,6 +159,8 @@
children = (
B834EFF81AC5FEEF00CF009E /* AssistantPlusApp.app */,
B834F0101AC5FEEF00CF009E /* AssistantPlusAppTests.xctest */,
+ B86E66EA1ACE7A77007C6014 /* assistantapp.app */,
+ B86E67021ACE7A77007C6014 /* assistantappTests.xctest */,
);
name = Products;
sourceTree = "";
@@ -133,6 +174,8 @@
B88C0A7E1ABF79AA00D4D107 /* MainViewController.h */,
B88C0A7F1ABF79AA00D4D107 /* MainViewController.m */,
B834EFF31AC5EF2C00CF009E /* LaunchScreen2.xib */,
+ B86E66D81ACD1991007C6014 /* PluginsViewController.h */,
+ B86E66D91ACD1991007C6014 /* PluginsViewController.m */,
B88C0ADE1AC32C2000D4D107 /* CustomRepliesViewController.h */,
B88C0ADF1AC32C2000D4D107 /* CustomRepliesViewController.m */,
B88C0AE11AC32C2A00D4D107 /* CustomReplyDetailViewController.h */,
@@ -190,6 +233,41 @@
productReference = B834F0101AC5FEEF00CF009E /* AssistantPlusAppTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
+ B86E66E91ACE7A77007C6014 /* assistantapp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = B86E670A1ACE7A77007C6014 /* Build configuration list for PBXNativeTarget "assistantapp" */;
+ buildPhases = (
+ B86E66E61ACE7A77007C6014 /* Sources */,
+ B86E66E71ACE7A77007C6014 /* Frameworks */,
+ B86E66E81ACE7A77007C6014 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = assistantapp;
+ productName = assistantapp;
+ productReference = B86E66EA1ACE7A77007C6014 /* assistantapp.app */;
+ productType = "com.apple.product-type.application";
+ };
+ B86E67011ACE7A77007C6014 /* assistantappTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = B86E670D1ACE7A77007C6014 /* Build configuration list for PBXNativeTarget "assistantappTests" */;
+ buildPhases = (
+ B86E66FE1ACE7A77007C6014 /* Sources */,
+ B86E66FF1ACE7A77007C6014 /* Frameworks */,
+ B86E67001ACE7A77007C6014 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ B86E67041ACE7A77007C6014 /* PBXTargetDependency */,
+ );
+ name = assistantappTests;
+ productName = assistantappTests;
+ productReference = B86E67021ACE7A77007C6014 /* assistantappTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -206,6 +284,14 @@
CreatedOnToolsVersion = 6.2;
TestTargetID = B834EFF71AC5FEEF00CF009E;
};
+ B86E66E91ACE7A77007C6014 = {
+ CreatedOnToolsVersion = 6.1;
+ DevelopmentTeam = 7AMDTT3YCW;
+ };
+ B86E67011ACE7A77007C6014 = {
+ CreatedOnToolsVersion = 6.1;
+ TestTargetID = B86E66E91ACE7A77007C6014;
+ };
};
};
buildConfigurationList = B88C0A591ABF794E00D4D107 /* Build configuration list for PBXProject "AssistantPlusApp" */;
@@ -223,6 +309,8 @@
targets = (
B834EFF71AC5FEEF00CF009E /* AssistantPlusApp */,
B834F00F1AC5FEEF00CF009E /* AssistantPlusAppTests */,
+ B86E66E91ACE7A77007C6014 /* assistantapp */,
+ B86E67011ACE7A77007C6014 /* assistantappTests */,
);
};
/* End PBXProject section */
@@ -242,6 +330,21 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ B86E66E81ACE7A77007C6014 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B86E671B1ACE7C66007C6014 /* Default-568h@2x.png in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ B86E67001ACE7A77007C6014 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -254,6 +357,7 @@
B8EC46D61AC6422100ED3836 /* ActivatorListenersViewController.m in Sources */,
B8EC46D71AC6422100ED3836 /* CustomRepliesViewController.m in Sources */,
B8EC46D81AC6422100ED3836 /* CustomReplyDetailViewController.m in Sources */,
+ B86E66DA1ACD1991007C6014 /* PluginsViewController.m in Sources */,
B8EC46D91AC6422100ED3836 /* ListenerDetailViewController.m in Sources */,
B8EC46DA1AC6422100ED3836 /* APActivatorListener.m in Sources */,
B8EC46DB1AC6422100ED3836 /* APCustomReply.m in Sources */,
@@ -269,6 +373,30 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ B86E66E61ACE7A77007C6014 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B86E67101ACE7AC0007C6014 /* AppDelegate.m in Sources */,
+ B86E67111ACE7AC0007C6014 /* MainViewController.m in Sources */,
+ B86E67121ACE7AC0007C6014 /* PluginsViewController.m in Sources */,
+ B86E67131ACE7AC0007C6014 /* CustomRepliesViewController.m in Sources */,
+ B86E67141ACE7AC0007C6014 /* CustomReplyDetailViewController.m in Sources */,
+ B86E67151ACE7AC0007C6014 /* ActivatorListenersViewController.m in Sources */,
+ B86E67161ACE7AC0007C6014 /* ListenerDetailViewController.m in Sources */,
+ B86E67171ACE7AC0007C6014 /* APActivatorListener.m in Sources */,
+ B86E67181ACE7AC0007C6014 /* APCustomReply.m in Sources */,
+ B86E67191ACE7AC0007C6014 /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ B86E66FE1ACE7A77007C6014 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -277,6 +405,11 @@
target = B834EFF71AC5FEEF00CF009E /* AssistantPlusApp */;
targetProxy = B834F0111AC5FEEF00CF009E /* PBXContainerItemProxy */;
};
+ B86E67041ACE7A77007C6014 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = B86E66E91ACE7A77007C6014 /* assistantapp */;
+ targetProxy = B86E67031ACE7A77007C6014 /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -352,6 +485,82 @@
};
name = Release;
};
+ B86E670B1ACE7A77007C6014 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = "$(SRCROOT)/Resources/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ B86E670C1ACE7A77007C6014 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ INFOPLIST_FILE = "$(SRCROOT)/Resources/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ B86E670E1ACE7A77007C6014 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(inherited)",
+ );
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = assistantappTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/assistantapp.app/assistantapp";
+ };
+ name = Debug;
+ };
+ B86E670F1ACE7A77007C6014 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ COPY_PHASE_STRIP = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = assistantappTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/assistantapp.app/assistantapp";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
B88C0A701ABF794E00D4D107 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -447,6 +656,22 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ B86E670A1ACE7A77007C6014 /* Build configuration list for PBXNativeTarget "assistantapp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B86E670B1ACE7A77007C6014 /* Debug */,
+ B86E670C1ACE7A77007C6014 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ };
+ B86E670D1ACE7A77007C6014 /* Build configuration list for PBXNativeTarget "assistantappTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B86E670E1ACE7A77007C6014 /* Debug */,
+ B86E670F1ACE7A77007C6014 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ };
B88C0A591ABF794E00D4D107 /* Build configuration list for PBXProject "AssistantPlusApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
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 4bf4fe4..a2ea630 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/xcuserdata/Zaid.xcuserdatad/xcschemes/assistantapp.xcscheme b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/assistantapp.xcscheme
new file mode 100644
index 0000000..7147620
--- /dev/null
+++ b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/assistantapp.xcscheme
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/xcschememanagement.plist b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/xcschememanagement.plist
index 65a95b6..f39fb6e 100644
--- a/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/assistant+/assistantplusapp/AssistantPlusApp.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -9,6 +9,11 @@
orderHint
0
+ assistantapp.xcscheme
+
+ orderHint
+ 1
+
SuppressBuildableAutocreation
@@ -47,6 +52,16 @@
primary
+ B86E66E91ACE7A77007C6014
+
+ primary
+
+
+ B86E67011ACE7A77007C6014
+
+ primary
+
+
B88C0A5D1ABF794E00D4D107
primary
diff --git a/assistant+/assistantplusapp/CustomRepliesViewController.m b/assistant+/assistantplusapp/CustomRepliesViewController.m
index 65181b9..7338aac 100644
--- a/assistant+/assistantplusapp/CustomRepliesViewController.m
+++ b/assistant+/assistantplusapp/CustomRepliesViewController.m
@@ -20,6 +20,8 @@
- (void)viewDidLoad {
[super viewDidLoad];
+
+ self.title = @"Custom Replies";
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewReply:)];
@@ -34,7 +36,7 @@
self.repliesTableView.backgroundColor = backgroundColor;
[self.view addSubview:self.repliesTableView];
-
+
[self loadRepliesFromFile];
}
@@ -55,7 +57,6 @@
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:@"customReplies"]) {
NSArray *replies = [defaults objectForKey:@"customReplies"];
- NSLog(@"Serialized replies: %@", replies);
for (NSDictionary *currReply in replies) {
APCustomReply *reply = [[APCustomReply alloc] initWithDictionary:currReply];
[savedReplies addObject:reply];
@@ -68,7 +69,6 @@
if (savedReplies) {
NSMutableArray *toSave = [[NSMutableArray alloc] init];
for (APCustomReply *currReply in savedReplies) {
- NSLog(@"Saving: %@ %@", currReply.trigger, currReply.response);
[toSave addObject:[currReply dictionaryRepresentation]];
}
@@ -78,8 +78,6 @@
CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"];
[center sendMessageName:@"UpdateCustomReplies" userInfo:@{@"customReplies" : toSave}];
}
-
- NSLog(@"Saved replies is: %@", savedReplies);
}
#pragma mark - Reply Creation
diff --git a/assistant+/assistantplusapp/CustomReplyDetailViewController.h b/assistant+/assistantplusapp/CustomReplyDetailViewController.h
index d792c97..925152e 100644
--- a/assistant+/assistantplusapp/CustomReplyDetailViewController.h
+++ b/assistant+/assistantplusapp/CustomReplyDetailViewController.h
@@ -13,7 +13,7 @@
- (void)customReplyDidChange:(APCustomReply*)reply;
@end
-@interface CustomReplyDetailViewController : UIViewController
+@interface CustomReplyDetailViewController : UIViewController
- (id)initWithCustomReply:(APCustomReply*)reply;
@property (weak) id delegate;
@end
diff --git a/assistant+/assistantplusapp/CustomReplyDetailViewController.m b/assistant+/assistantplusapp/CustomReplyDetailViewController.m
index 936e9d9..4211d49 100644
--- a/assistant+/assistantplusapp/CustomReplyDetailViewController.m
+++ b/assistant+/assistantplusapp/CustomReplyDetailViewController.m
@@ -11,7 +11,7 @@
@interface CustomReplyDetailViewController ()
@property (strong, nonatomic) APCustomReply *currReply;
@property (strong, nonatomic) UITextField *triggerField;
-@property (strong, nonatomic) UITextField *responseField;
+@property (strong, nonatomic) UITextView *responseField;
@property (nonatomic) BOOL didChange;
@end
@@ -46,12 +46,13 @@
[triggerBackground addSubview:self.triggerField];
[self.view addSubview:triggerBackground];
- UIView *responseBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 190, self.view.frame.size.width, 50)];
+ UIView *responseBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 190, self.view.frame.size.width, 100)];
responseBackground.backgroundColor = [UIColor whiteColor];
UILabel *responseLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 100, 50)];
responseLabel.text = @"Response:";
- self.responseField = [[UITextField alloc] initWithFrame:CGRectMake(120, 2, self.view.frame.size.width-60, 50)];
+ self.responseField = [[UITextView alloc] initWithFrame:CGRectMake(110, 8, self.view.frame.size.width-110, 90)];
self.responseField.text = self.currReply.response;
+ self.responseField.font = [UIFont systemFontOfSize:16];
self.responseField.delegate = self;
[responseBackground addSubview:responseLabel];
[responseBackground addSubview:self.responseField];
@@ -62,12 +63,9 @@
[super viewWillDisappear:animated];
if (self.didChange) {
- NSLog(@"Did change!");
self.currReply.trigger = self.triggerField.text;
self.currReply.response = self.responseField.text;
[self.delegate customReplyDidChange:self.currReply];
- } else {
- NSLog(@"Didn't change!");
}
}
@@ -75,7 +73,11 @@
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
self.didChange = YES;
- return YES;
+ return YES;
}
+- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
+ self.didChange = YES;
+ return YES;
+}
@end
diff --git a/assistant+/assistantplusapp/ListenerDetailViewController.h b/assistant+/assistantplusapp/ListenerDetailViewController.h
index b4a2d1c..23593ce 100644
--- a/assistant+/assistantplusapp/ListenerDetailViewController.h
+++ b/assistant+/assistantplusapp/ListenerDetailViewController.h
@@ -9,6 +9,11 @@
#import
#import "APActivatorListener.h"
-@interface ListenerDetailViewController : UIViewController
+@protocol ListenerDetailDelegate
+- (void)listenerDidChange:(APActivatorListener*)listener;
+@end
+
+@interface ListenerDetailViewController : UIViewController
+@property (assign) id delegate;
- (id)initWithListener:(APActivatorListener*)listener;
@end
diff --git a/assistant+/assistantplusapp/ListenerDetailViewController.m b/assistant+/assistantplusapp/ListenerDetailViewController.m
index 2d6f26a..9633963 100644
--- a/assistant+/assistantplusapp/ListenerDetailViewController.m
+++ b/assistant+/assistantplusapp/ListenerDetailViewController.m
@@ -12,7 +12,8 @@
@property (strong, nonatomic) APActivatorListener *currListener;
@property (strong, nonatomic) UISwitch *enabledSwitch;
@property (strong, nonatomic) UITextField *nameField;
-@property (strong, nonatomic) UITextField *triggerField;
+@property (strong, nonatomic) UITextView *triggerField;
+@property (nonatomic) BOOL didChange;
@end
@implementation ListenerDetailViewController
@@ -20,6 +21,7 @@
- (id)initWithListener:(APActivatorListener*)listener {
if (self = [super init]) {
self.currListener = listener;
+ self.didChange = NO;
}
return self;
}
@@ -29,7 +31,7 @@
UIColor *backgroundColor = [UIColor colorWithWhite:.9f alpha:1.0];
self.view.backgroundColor = backgroundColor;
- UIView *switchBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, 50)];
+ UIView *switchBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 90, self.view.frame.size.width, 50)];
switchBackground.backgroundColor = [UIColor whiteColor];
UILabel *switchLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 80, 50)];
switchLabel.text = @"Enabled:";
@@ -40,7 +42,7 @@
[switchBackground addSubview:self.enabledSwitch];
[self.view addSubview:switchBackground];
- UIView *nameBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 190, self.view.frame.size.width, 50)];
+ UIView *nameBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 170, self.view.frame.size.width, 50)];
nameBackground.backgroundColor = [UIColor whiteColor];
UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 60, 50)];
nameLabel.text = @"Name:";
@@ -52,11 +54,12 @@
[self.view addSubview:nameBackground];
- UIView *triggerBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 280, self.view.frame.size.width, 50)];
+ UIView *triggerBackground = [[UIView alloc] initWithFrame:CGRectMake(0, 230, self.view.frame.size.width, 100)];
triggerBackground.backgroundColor = [UIColor whiteColor];
UILabel *triggerLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, 70, 50)];
triggerLabel.text = @"Trigger:";
- self.triggerField = [[UITextField alloc] initWithFrame:CGRectMake(90, 2, self.view.frame.size.width-60, 50)];
+ self.triggerField = [[UITextView alloc] initWithFrame:CGRectMake(80, 8, self.view.frame.size.width-80, 90)];
+ self.triggerField.font = [UIFont systemFontOfSize:16];
self.triggerField.text = self.currListener.trigger;
self.triggerField.delegate = self;
[triggerBackground addSubview:triggerLabel];
@@ -64,18 +67,31 @@
[self.view addSubview:triggerBackground];
}
+- (void)viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+
+ if (self.didChange) {
+ self.currListener.trigger = self.triggerField.text;
+ self.currListener.name = self.nameField.text;
+ [self.delegate listenerDidChange:self.currListener];
+ }
+}
+
#pragma mark - UI Delegates
- (void)didToggleSwitch:(UISwitch*)theSwitch {
+ self.didChange = YES;
self.currListener.enabled = theSwitch.on;
}
-- (void)textFieldDidEndEditing:(UITextField *)textField {
- if (textField == self.triggerField) {
- self.currListener.trigger = textField.text;
- } else if (textField == self.nameField) {
- self.currListener.name = textField.text;
- }
+- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
+ self.didChange = YES;
+ return YES;
+}
+
+- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
+ self.didChange = YES;
+ return YES;
}
- (void)didReceiveMemoryWarning {
diff --git a/assistant+/assistantplusapp/MainViewController.m b/assistant+/assistantplusapp/MainViewController.m
index e64b4c4..883b89f 100644
--- a/assistant+/assistantplusapp/MainViewController.m
+++ b/assistant+/assistantplusapp/MainViewController.m
@@ -1,6 +1,8 @@
#import "MainViewController.h"
#import "ActivatorListenersViewController.h"
#import "CustomRepliesViewController.h"
+#import "CPDistributedMessagingCenter.h"
+#import "PluginsViewController.h"
@implementation MainViewController
@@ -23,17 +25,6 @@
self.optionsTable.backgroundColor = backgroundColor;
[self.view addSubview:self.optionsTable];
-
- NSBundle *bundle = [NSBundle mainBundle];
- NSLog(@"%@", bundle);
- NSLog(@"%@", bundle.resourcePath);
-
- NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
- NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:bundleURL
- includingPropertiesForKeys:@[]
- options:NSDirectoryEnumerationSkipsHiddenFiles
- error:nil];
- NSLog(@"contents: %@", contents);
}
- (void)viewWillAppear:(BOOL)animated {
@@ -106,9 +97,11 @@
case 1:
[self goToNewVC:[[CustomRepliesViewController alloc] init]];
break;
- case 2:
- [self goToNewVC:[[ActivatorListenersViewController alloc] init]];
- break;
+ case 2: {
+ CPDistributedMessagingCenter *center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"];
+ NSDictionary *installed = [center sendMessageAndReceiveReplyName:@"getInstalledPlugins" userInfo:nil];
+ [self goToNewVC:[[PluginsViewController alloc] initWithInstalledPlugins:installed[@"plugins"]]];
+ break; }
default:
break;
}
diff --git a/assistant+/assistantplusapp/Makefile b/assistant+/assistantplusapp/Makefile
index 0d6b999..dfd01e2 100644
--- a/assistant+/assistantplusapp/Makefile
+++ b/assistant+/assistantplusapp/Makefile
@@ -1,13 +1,11 @@
include theos/makefiles/common.mk
-export THEOS_DEVICE_IP=192.168.1.17
-
export ARCHS = armv7 arm64
export TARGET = iphone:clang:latest:8.0
-export SDKVERSION=8.2
+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
+AssistantPlusApp_FILES = main.m AppDelegate.m MainViewController.m APActivatorListener.m ActivatorListenersViewController.m ListenerDetailViewController.m CustomReplyDetailViewController.m CustomRepliesViewController.m APCustomReply.m PluginsViewController.m
AssistantPlusApp_FRAMEWORKS = UIKit CoreGraphics QuartzCore
AssistantPlusApp_PRIVATE_FRAMEWORKS = AppSupport
AssistantPlusApp_CFLAGS = -fobjc-arc
@@ -15,4 +13,4 @@ AssistantPlusApp_CFLAGS = -fobjc-arc
include $(THEOS_MAKE_PATH)/application.mk
after-install::
- install.exec “uicache”
\ No newline at end of file
+ install.exec "killall -9 SpringBoard"
\ No newline at end of file
diff --git a/assistant+/assistantplusapp/PluginsViewController.h b/assistant+/assistantplusapp/PluginsViewController.h
new file mode 100644
index 0000000..86afcc8
--- /dev/null
+++ b/assistant+/assistantplusapp/PluginsViewController.h
@@ -0,0 +1,14 @@
+//
+// PluginsViewController.h
+// AssistantPlusApp
+//
+// Created by Zaid Elkurdi on 4/1/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import
+
+@interface PluginsViewController : UIViewController
+@property (strong, nonatomic) UITableView *pluginsTable;
+- (id)initWithInstalledPlugins:(NSArray*)plugins;
+@end
diff --git a/assistant+/assistantplusapp/PluginsViewController.m b/assistant+/assistantplusapp/PluginsViewController.m
new file mode 100644
index 0000000..813ab4f
--- /dev/null
+++ b/assistant+/assistantplusapp/PluginsViewController.m
@@ -0,0 +1,65 @@
+//
+// PluginsViewController.m
+// AssistantPlusApp
+//
+// Created by Zaid Elkurdi on 4/1/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import "PluginsViewController.h"
+
+@implementation PluginsViewController {
+ NSArray *installedPlugins;
+}
+
+- (id)initWithInstalledPlugins:(NSArray *)plugins {
+ if (self = [super init]) {
+ installedPlugins = plugins;
+ }
+ return self;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.pluginsTable = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
+ self.pluginsTable.delegate = self;
+ self.pluginsTable.dataSource = self;
+ self.pluginsTable.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
+ [self.view addSubview:self.pluginsTable];
+
+ UIColor *backgroundColor = [UIColor colorWithWhite:.9f alpha:1.0];
+ self.pluginsTable.backgroundColor = backgroundColor;
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+#pragma mark - UITableViewDelegate
+
+- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"pluginCell"];
+ if (!cell) {
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"pluginCell"];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ }
+
+ NSDictionary *currPlugin = [installedPlugins objectAtIndex:indexPath.row];
+ cell.textLabel.text = currPlugin[@"name"];
+ cell.detailTextLabel.text = currPlugin[@"author"];
+ return cell;
+}
+
+#pragma mark - UITableViewDataSource
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return installedPlugins.count;
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return 60.0f;
+}
+
+@end
diff --git a/assistant+/assistantplusapp/Resources/Default-568h@2x.png b/assistant+/assistantplusapp/Resources/Default-568h@2x.png
index beb6512..5985e80 100644
Binary files a/assistant+/assistantplusapp/Resources/Default-568h@2x.png and b/assistant+/assistantplusapp/Resources/Default-568h@2x.png differ
diff --git a/assistant+/assistantplusapp/Resources/Default-667h@2x.png b/assistant+/assistantplusapp/Resources/Default-667h@2x.png
deleted file mode 100644
index b27e252..0000000
Binary files a/assistant+/assistantplusapp/Resources/Default-667h@2x.png and /dev/null differ
diff --git a/assistant+/assistantplusapp/Resources/Default-700-568h@2x.png b/assistant+/assistantplusapp/Resources/Default-700-568h@2x.png
new file mode 100755
index 0000000..0f5aa5f
Binary files /dev/null and b/assistant+/assistantplusapp/Resources/Default-700-568h@2x.png differ
diff --git a/assistant+/assistantplusapp/Resources/Default-700@2x.png b/assistant+/assistantplusapp/Resources/Default-700@2x.png
new file mode 100755
index 0000000..42a3e34
Binary files /dev/null and b/assistant+/assistantplusapp/Resources/Default-700@2x.png differ
diff --git a/assistant+/assistantplusapp/Resources/Default-736h@3x.png b/assistant+/assistantplusapp/Resources/Default-736h@3x.png
deleted file mode 100644
index adce1f8..0000000
Binary files a/assistant+/assistantplusapp/Resources/Default-736h@3x.png and /dev/null differ
diff --git a/assistant+/assistantplusapp/Resources/Default-800-667h@2x.png b/assistant+/assistantplusapp/Resources/Default-800-667h@2x.png
new file mode 100755
index 0000000..bdf275c
Binary files /dev/null and b/assistant+/assistantplusapp/Resources/Default-800-667h@2x.png differ
diff --git a/assistant+/assistantplusapp/Resources/Default-800-667h@3x.png b/assistant+/assistantplusapp/Resources/Default-800-667h@3x.png
new file mode 100755
index 0000000..6602ea2
Binary files /dev/null and b/assistant+/assistantplusapp/Resources/Default-800-667h@3x.png differ
diff --git a/assistant+/assistantplusapp/Resources/Default-800-Portrait-736h@3x.png b/assistant+/assistantplusapp/Resources/Default-800-Portrait-736h@3x.png
new file mode 100755
index 0000000..5591fc3
Binary files /dev/null and b/assistant+/assistantplusapp/Resources/Default-800-Portrait-736h@3x.png differ
diff --git a/assistant+/assistantplusapp/Resources/Default@2x.png b/assistant+/assistantplusapp/Resources/Default@2x.png
deleted file mode 100644
index e56f94e..0000000
Binary files a/assistant+/assistantplusapp/Resources/Default@2x.png and /dev/null differ
diff --git a/assistant+/assistantplusapp/Resources/Icon.png b/assistant+/assistantplusapp/Resources/Icon.png
new file mode 100644
index 0000000..a07e3f1
Binary files /dev/null and b/assistant+/assistantplusapp/Resources/Icon.png differ
diff --git a/assistant+/assistantplusapp/Resources/Info.plist b/assistant+/assistantplusapp/Resources/Info.plist
index c39ecb6..71021ea 100644
--- a/assistant+/assistantplusapp/Resources/Info.plist
+++ b/assistant+/assistantplusapp/Resources/Info.plist
@@ -4,8 +4,6 @@
CFBundleDisplayName
Assistant+
- UIStatusBarStyle
- UIStatusBarStyleDefault
CFBundleDevelopmentRegion
en
CFBundleSupportedPlatforms
@@ -23,7 +21,7 @@
1
LSRequiresIPhoneOS
-
+
CFBundlePackageType
APPL
CFBundleShortVersionString
@@ -34,27 +32,25 @@
8.0
CFBundleVersion
1
- UIStatusBarTintParameters
-
- UINavigationBar
-
- Style
- UIBarStyleDefault
- Translucent
- false
-
-
- UIStatusBarHidden
-
- UILaunchImageFile
- Default.png
+ CFBundleIconFile
+ Icon.png
UILaunchImages
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default
+ Default-568h
+ UILaunchImageOrientation
+ Portrait
+ UILaunchImageSize
+ {640, 1136}
+
+
+ UILaunchImageMinimumOSVersion
+ 7.0
+ UILaunchImageName
+ Default-700
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -62,19 +58,9 @@
UILaunchImageMinimumOSVersion
- 8.0
+ 7.0
UILaunchImageName
- Default
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {320, 480}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-568h
+ Default-700-568h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -84,17 +70,7 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-568h
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {320, 568}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-667h
+ Default-800-667h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -104,17 +80,7 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-667h
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {375, 667}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-736h
+ Default-800-Portrait-736h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -124,35 +90,13 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-736h
+ Default-800-Landscape-736h
UILaunchImageOrientation
Landscape
UILaunchImageSize
{414, 736}
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-Portrait
- UILaunchImageOrientation
- Portrait
- UILaunchImageSize
- {768, 1024}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-Landscape
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {768, 1024}
-
- CFBundleIconFile
- Icon.png
CFBundleIconFiles
Icon.png
diff --git a/assistant+/assistantplusapp/Resources/LaunchScreen.nib b/assistant+/assistantplusapp/Resources/LaunchScreen.nib
deleted file mode 100644
index 1d22d8b..0000000
Binary files a/assistant+/assistantplusapp/Resources/LaunchScreen.nib and /dev/null differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/AssistantPlusApp b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/AssistantPlusApp
index 670e887..bb7021a 100755
Binary files a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/AssistantPlusApp and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/AssistantPlusApp differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-568h@2x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-568h@2x.png
index beb6512..5985e80 100644
Binary files a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-568h@2x.png and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-568h@2x.png differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-667h@2x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-667h@2x.png
deleted file mode 100644
index b27e252..0000000
Binary files a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-667h@2x.png and /dev/null differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-700-568h@2x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-700-568h@2x.png
new file mode 100755
index 0000000..0f5aa5f
Binary files /dev/null and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-700-568h@2x.png differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-700@2x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-700@2x.png
new file mode 100755
index 0000000..42a3e34
Binary files /dev/null and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-700@2x.png differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-736h@3x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-736h@3x.png
deleted file mode 100644
index adce1f8..0000000
Binary files a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-736h@3x.png and /dev/null differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-667h@2x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-667h@2x.png
new file mode 100755
index 0000000..bdf275c
Binary files /dev/null and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-667h@2x.png differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-667h@3x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-667h@3x.png
new file mode 100755
index 0000000..6602ea2
Binary files /dev/null and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-667h@3x.png differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-Portrait-736h@3x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-Portrait-736h@3x.png
new file mode 100755
index 0000000..5591fc3
Binary files /dev/null and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default-800-Portrait-736h@3x.png differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default@2x.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default@2x.png
deleted file mode 100644
index e56f94e..0000000
Binary files a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Default@2x.png and /dev/null differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Icon.png b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Icon.png
new file mode 100644
index 0000000..a07e3f1
Binary files /dev/null and b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Icon.png differ
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Info.plist b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Info.plist
index c39ecb6..71021ea 100644
--- a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Info.plist
+++ b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/Info.plist
@@ -4,8 +4,6 @@
CFBundleDisplayName
Assistant+
- UIStatusBarStyle
- UIStatusBarStyleDefault
CFBundleDevelopmentRegion
en
CFBundleSupportedPlatforms
@@ -23,7 +21,7 @@
1
LSRequiresIPhoneOS
-
+
CFBundlePackageType
APPL
CFBundleShortVersionString
@@ -34,27 +32,25 @@
8.0
CFBundleVersion
1
- UIStatusBarTintParameters
-
- UINavigationBar
-
- Style
- UIBarStyleDefault
- Translucent
- false
-
-
- UIStatusBarHidden
-
- UILaunchImageFile
- Default.png
+ CFBundleIconFile
+ Icon.png
UILaunchImages
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default
+ Default-568h
+ UILaunchImageOrientation
+ Portrait
+ UILaunchImageSize
+ {640, 1136}
+
+
+ UILaunchImageMinimumOSVersion
+ 7.0
+ UILaunchImageName
+ Default-700
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -62,19 +58,9 @@
UILaunchImageMinimumOSVersion
- 8.0
+ 7.0
UILaunchImageName
- Default
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {320, 480}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-568h
+ Default-700-568h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -84,17 +70,7 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-568h
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {320, 568}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-667h
+ Default-800-667h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -104,17 +80,7 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-667h
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {375, 667}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-736h
+ Default-800-Portrait-736h
UILaunchImageOrientation
Portrait
UILaunchImageSize
@@ -124,35 +90,13 @@
UILaunchImageMinimumOSVersion
8.0
UILaunchImageName
- Default-736h
+ Default-800-Landscape-736h
UILaunchImageOrientation
Landscape
UILaunchImageSize
{414, 736}
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-Portrait
- UILaunchImageOrientation
- Portrait
- UILaunchImageSize
- {768, 1024}
-
-
- UILaunchImageMinimumOSVersion
- 8.0
- UILaunchImageName
- Default-Landscape
- UILaunchImageOrientation
- Landscape
- UILaunchImageSize
- {768, 1024}
-
- CFBundleIconFile
- Icon.png
CFBundleIconFiles
Icon.png
diff --git a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/LaunchScreen.nib b/assistant+/assistantplusapp/obj/AssistantPlusApp.app/LaunchScreen.nib
deleted file mode 100644
index 1d22d8b..0000000
Binary files a/assistant+/assistantplusapp/obj/AssistantPlusApp.app/LaunchScreen.nib and /dev/null differ
diff --git a/assistant+/assistantpluspluginmanager/APActivatorListener.h b/assistant+/assistantpluspluginmanager/APActivatorListener.h
new file mode 100644
index 0000000..65b5811
--- /dev/null
+++ b/assistant+/assistantpluspluginmanager/APActivatorListener.h
@@ -0,0 +1,17 @@
+//
+// APActivatorListener.h
+//
+//
+// Created by Zaid Elkurdi on 4/2/15.
+//
+//
+
+#import
+
+@interface APActivatorListener : NSObject
+@property (strong, nonatomic) NSString *name;
+@property (strong, nonatomic) NSString *triggerString;
+@property (strong, nonatomic) NSRegularExpression *trigger;
+@property (strong, nonatomic) NSString *identifier;
+- (id)initWithDictionary:(NSDictionary*)dict;
+@end
diff --git a/assistant+/assistantpluspluginmanager/APActivatorListener.m b/assistant+/assistantpluspluginmanager/APActivatorListener.m
new file mode 100644
index 0000000..dc604f0
--- /dev/null
+++ b/assistant+/assistantpluspluginmanager/APActivatorListener.m
@@ -0,0 +1,22 @@
+//
+// APActivatorListener.m
+//
+//
+// Created by Zaid Elkurdi on 4/2/15.
+//
+//
+
+#import "APActivatorListener.h"
+
+@implementation APActivatorListener
+
+- (id)initWithDictionary:(NSDictionary*)dict {
+ if (self = [super init]) {
+ self.name = dict[@"name"];
+ self.triggerString = dict[@"trigger"];
+ self.trigger = [NSRegularExpression regularExpressionWithPattern:self.triggerString options:NSRegularExpressionCaseInsensitive error:nil];
+ self.identifier = dict[@"identifier"];
+ }
+ return self;
+}
+@end
diff --git a/assistant+/assistantpluspluginmanager/APPlugin.h b/assistant+/assistantpluspluginmanager/APPlugin.h
index 585bfa9..08b7953 100644
--- a/assistant+/assistantpluspluginmanager/APPlugin.h
+++ b/assistant+/assistantpluspluginmanager/APPlugin.h
@@ -10,8 +10,6 @@
@interface APPlugin : NSObject {
NSBundle *bundle;
- BOOL isInitialized;
-
NSString *name;
NSString *displayName;
NSString *bundleName;
@@ -25,6 +23,7 @@
id pluginClass;
}
-(NSString*)displayName;
+-(NSString*)author;
-(NSString*)identifier;
- (id)initWithFilePath:(NSURL*)filePath andName:(NSString*)name;
diff --git a/assistant+/assistantpluspluginmanager/APPlugin.m b/assistant+/assistantpluspluginmanager/APPlugin.m
index 07b1386..77c8d68 100644
--- a/assistant+/assistantpluspluginmanager/APPlugin.m
+++ b/assistant+/assistantpluspluginmanager/APPlugin.m
@@ -16,55 +16,45 @@
commands = [[NSMutableArray alloc] init];
snippets = [[NSMutableSet alloc] init];
- NSLog(@"Commnds just created: %@", commands);
-
bundle = [NSBundle bundleWithURL:filePath];
if (!bundle) {
- NSLog(@"Failed to open extension bundle %@ (%@)!", fileName, filePath);
+ NSLog(@"Failed to open plugin bundle %@!", filePath);
return nil;
}
if (![bundle load]) {
- NSLog(@"Failed to load extension bundle %@ (wrong CFBundleExecutable? Missing? Not signed?)!", name);
+ NSLog(@"Failed to load plugin bundle %@!", name);
return nil;
- } else {
- NSLog(@"Loaded bundle!");
}
//load principal class
Class principal = [bundle principalClass];
if (!principal) {
- NSLog(@"Plugin %@ doesn't provide a NSPrincipalClass!", fileName);
+ NSLog(@"AP: Plugin %@ doesn't provide a NSPrincipalClass!", fileName);
return nil;
}
NSLog(@"AP: Principal Class is %@", principal);
- pluginClass = [[principal alloc] initWithSystem:self];
+ pluginClass = [[principal alloc] initWithPluginManager:self];
if (!pluginClass) {
- NSLog(@"Failed to initialize NSPrincipalClass from plugin %@!", fileName);
+ NSLog(@"AP: Failed to initialize NSPrincipalClass from plugin %@!", fileName);
return nil;
- } else {
- NSLog(@"has pluginClass!");
}
- // get extension info
- displayName = @"FuckerShit";
-// displayName = [[[_bundle infoDictionary] objectForKey:@"APPluginName"] copy];
-// if (!displayName) {
-// displayName = name;
-// }
+ //Get the plugin's display name
+ displayName = [[bundle infoDictionary] objectForKey:@"APPluginName"];
+ if (!displayName) {
+ displayName = name;
+ }
- author = [[bundle objectForInfoDictionaryKey:@"PluginAuthor"] copy];
- pluginDescription = [[bundle objectForInfoDictionaryKey:@"PluginDescription"] copy];
- identifier = [[bundle objectForInfoDictionaryKey:@"CFBundleIdentifier"] copy];
+ author = [bundle objectForInfoDictionaryKey:@"APPluginAuthor"];
+ identifier = [bundle objectForInfoDictionaryKey:@"CFBundleIdentifier"];
pluginName = fileName;
- bundleName = [name copy];
- isInitialized = YES;
+ bundleName = fileName;
}
- NSLog(@"Loaded Plugin: %@", self);
return self;
}
@@ -72,17 +62,19 @@
return displayName;
}
+-(NSString*)author {
+ return author;
+}
+
- (NSString*)identifier {
return identifier;
}
- (NSArray*)getRegisteredCommands {
- NSLog(@"Registered Commands: %@", commands);
return commands;
}
- (NSSet*)getRegisteredSnippets {
- NSLog(@"Registered Snippets: %@", snippets);
return snippets;
}
@@ -107,17 +99,16 @@
NSObject* snip = [NSClassFromString(snippetClass) alloc];
id initRes = nil;
- if ([snip respondsToSelector:@selector(initWithProperties:system:)])
- initRes = [snip initWithProperties:props system:self];
- if (!initRes && [snip respondsToSelector:@selector(initWithProperties:)])
+ if (!initRes && [snip respondsToSelector:@selector(initWithProperties:)]) {
initRes = [snip initWithProperties:props];
+ }
- if (!initRes)
+ if (!initRes) {
initRes = [snip init];
+ }
- if (!initRes)
- {
+ if (!initRes) {
NSLog(@"APPluginSnippet class %@ failed to initialize!", snippetClass);
return nil;
}
@@ -136,23 +127,14 @@
return NO;
}
- // alloc
id inst = [cls alloc];
-
- // init 1.0.2
- if ([inst respondsToSelector:@selector(initWithSystem:)]) {
- inst = [inst initWithSystem:self];
- } else {
- NSLog(@"%@ did not respond to initWithSystem:. Using default init.", className);
- inst = [inst init];
- }
+ inst = [inst init];
if (!inst) {
NSLog(@"Command %@ failed to initialize!", className);
return NO;
}
-
[commands addObject:inst];
NSLog(@"Registered Command %@, commands is now: %@", className, commands);
@@ -168,21 +150,9 @@
return NO;
}
- id helloClass = [[NSClassFromString(className) alloc] init];
- NSLog(@"Snippet Initalized: %@", helloClass);
-
[snippets addObject:className];
- NSLog(@"Registered snippet %@, snippets is now %@", className, snippets);
return YES;
}
--(NSString*)systemVersion {
- return @"1.0";
-}
-
--(NSString*)localizedString:(NSString*)text {
- return text;
-}
-
@end
diff --git a/assistant+/assistantpluspluginmanager/APPluginSnippetViewController.xm b/assistant+/assistantpluspluginmanager/APPluginSnippetViewController.xm
index d54571a..fbe10a4 100644
--- a/assistant+/assistantpluspluginmanager/APPluginSnippetViewController.xm
+++ b/assistant+/assistantpluspluginmanager/APPluginSnippetViewController.xm
@@ -7,7 +7,6 @@ static UIViewController *_view;
%new
-(void)setCustomView:(UIViewController*)newVC {
- NSLog(@"Setting custom view to: %@", newVC);
_view = newVC;
[self addChildViewController:newVC];
[newVC didMoveToParentViewController:self];
@@ -17,13 +16,11 @@ static UIViewController *_view;
%new
-(id)viewControllerForSnippet:(id)arg1 error:(id)arg2 {
- NSLog(@"VC FOR Snippet: %@" ,arg1);
return _view;
}
%new
-(id)viewControllerForAceObject:(id)arg1 {
- NSLog(@"VC FOR ACE: %@" ,arg1);
return _view;
}
@@ -42,17 +39,13 @@ static UIViewController *_view;
}
-(id)navigationTitle {
- return @"yoyoyo";
+ return @"APPluginSnippet";
}
%new
-(void)transcriptViewControllerTappedOutsideEditingView {
- NSLog(@"tapped outside editing view!");
+ NSLog(@"Tapped outside editing view!");
}
-- (void)didMoveToParentViewController:(UIViewController *)parent {
- NSLog(@"Hello moved to %@", parent);
-}
-
%end
diff --git a/assistant+/assistantpluspluginmanager/APPluginSystem.h b/assistant+/assistantpluspluginmanager/APPluginSystem.h
index 612b0a1..2571740 100644
--- a/assistant+/assistantpluspluginmanager/APPluginSystem.h
+++ b/assistant+/assistantpluspluginmanager/APPluginSystem.h
@@ -12,13 +12,12 @@
@interface APPluginSystem : NSObject {
NSMutableArray *plugins;
- NSMutableDictionary *activatorListenersDict;
NSMutableArray *activatorListenersArray;
}
+ (id)sharedManager;
- (BOOL)loadPlugins;
- (BOOL)handleCommand:(NSString*)command withTokens:(NSSet*)tokens withSession:(APSession*)currSession;
-- (id)viewControllerForClass:(NSString*)snippetClass;
- (void)reloadCustomRepliesPlugin:(NSDictionary*)replies;
-- (void)reloadActivatorListeners;
+- (void)reloadActivatorListeners:(NSDictionary*)listeners;
+- (NSDictionary*)getInstalledPlugins;
@end
diff --git a/assistant+/assistantpluspluginmanager/APPluginSystem.m b/assistant+/assistantpluspluginmanager/APPluginSystem.m
index e42df12..5ff0238 100644
--- a/assistant+/assistantpluspluginmanager/APPluginSystem.m
+++ b/assistant+/assistantpluspluginmanager/APPluginSystem.m
@@ -7,7 +7,8 @@
//
#import "APPluginSystem.h"
-#include "APPlugin.h"
+#import "APPlugin.h"
+#import "APActivatorListener.h"
static NSString *PREFERENCE_PATH = @"/var/mobile/Library/Preferences/com.assistantplus.app.plist";
static NSString *EVENT_PREFIX = @"APListener";
@@ -19,14 +20,14 @@ static NSString *EVENT_PREFIX = @"APListener";
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
- NSLog(@"Creating plugin manager again!");
if ([sharedManager loadPlugins]) {
NSLog(@"Successfully loaded plugins!");
} else {
NSLog(@"Failed to load plugins!");
}
- [sharedManager reloadActivatorListeners];
+ NSDictionary *pref = [NSDictionary dictionaryWithContentsOfFile:PREFERENCE_PATH];
+ [sharedManager reloadActivatorListeners:pref];
});
return sharedManager;
}
@@ -42,13 +43,9 @@ static NSString *EVENT_PREFIX = @"APListener";
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
-// NSPredicate *predicate = [NSPredicate predicateWithFormat:@"pathExtension == 'assistantPlugin'"];
-
- NSLog(@"Loading Plugins:");
- for (NSURL *fileURL in contents) {//[contents filteredArrayUsingPredicate:predicate]) {
+ for (NSURL *fileURL in contents) {
NSString *name = [[[fileURL absoluteString] lastPathComponent] stringByDeletingPathExtension];
- NSLog(@"Loading %@ at %@", name, fileURL);
APPlugin *currPlugin = [[APPlugin alloc] initWithFilePath:fileURL andName:name];
if (currPlugin != nil) {
@@ -60,66 +57,43 @@ static NSString *EVENT_PREFIX = @"APListener";
}
- (BOOL)handleCommand:(NSString*)command withTokens:(NSSet*)tokens withSession:(APSession*)currSession {
- NSLog(@"Looking for command to handle: %@", command);
- NSLog(@"There are currently %lu plugins registered: %@", (unsigned long)plugins.count, plugins);
-
//First check activator listeners
- NSString *lowercase = [command lowercaseString];
- lowercase = [lowercase stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
- NSLog(@"Listeners: %@ Query: %@", activatorListenersDict, lowercase);
- if ([activatorListenersDict objectForKey:lowercase]) {
- NSLog(@"Handling with activator!");
- [LASharedActivator sendEventToListener:[LAEvent eventWithName:[activatorListenersDict objectForKey:lowercase] mode:LASharedActivator.currentEventMode]];
- return YES;
+ NSString *userCommand = [command lowercaseString];
+ userCommand = [userCommand stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ for (APActivatorListener *currListener in activatorListenersArray) {
+ NSArray *arrayOfAllMatches = [currListener.trigger matchesInString:userCommand options:0 range:NSMakeRange(0, [userCommand length])];
+ for (NSTextCheckingResult *match in arrayOfAllMatches) {
+ if (match.numberOfRanges > 0) {
+ NSString *eventName = [NSString stringWithFormat:@"%@%@", EVENT_PREFIX, currListener.identifier];
+ [LASharedActivator sendEventToListener:[LAEvent eventWithName:eventName mode:LASharedActivator.currentEventMode]];
+ [currSession sendRequestCompleted];
+ return YES;
+ }
+ }
}
+ NSLog(@"AP: Got Command \"%@\"", userCommand);
for (APPlugin *currPlugin in plugins) {
- NSLog(@"Currently on: %@:%@", currPlugin, [currPlugin displayName]);
- if ([currPlugin handleSpeech:command withTokens:tokens withSession:currSession]) {
- NSLog(@"%@ is handling command: %@", [currPlugin displayName], command);
+ if ([currPlugin handleSpeech:userCommand withTokens:tokens withSession:currSession]) {
return YES;
}
}
return NO;
}
-- (id)viewControllerForClass:(NSString*)snippetClass {
- NSLog(@"Begin search for: %@ with %d plugins", snippetClass, (int)plugins.count);
+#pragma mark - Message Handlers
+
+- (NSDictionary*)getInstalledPlugins {
+ NSMutableArray *pluginArray = [[NSMutableArray alloc] init];
for (APPlugin *currPlugin in plugins) {
- NSLog(@"Current (%@) contains: %@", [currPlugin displayName], [currPlugin getRegisteredSnippets]);
- if ([[currPlugin getRegisteredSnippets] containsObject:snippetClass]) {
- NSObject* snip = [NSClassFromString(snippetClass) alloc];
-
- id initRes = nil;
-
- if ([snip respondsToSelector:@selector(initWithProperties:)])
- initRes = [snip initWithProperties:@{@"labelText" : @"fuck you"}];
-
- if (!initRes)
- initRes = [snip init];
-
- if (!initRes) {
- NSLog(@"ERROR: Snippet class %@ failed to initialize!", snippetClass);
- return nil;
- }
- return snip;
- }
+ NSDictionary *currDict = @{@"name" : [currPlugin displayName],
+ @"author" :[currPlugin author]};
+ [pluginArray addObject:currDict];
}
- NSLog(@"APPluginManager: Found no VC for %@", snippetClass);
- return nil;
+ return @{@"plugins" : pluginArray};
}
--(NSString*)localizedString:(NSString*)text {
- return text;
-}
-
--(NSString*)systemVersion {
- return @"1.0";
-}
-
-#pragma mark - Custom replies
- (void)reloadCustomRepliesPlugin:(NSDictionary*)replies {
- NSLog(@"Updating replies with %@", replies);
for (APPlugin *currPlugin in plugins) {
if ([[currPlugin identifier] isEqualToString:@"com.assistantplus.customreplyidentifier"]) {
id customCmd = [[currPlugin getRegisteredCommands] lastObject];
@@ -131,37 +105,27 @@ static NSString *EVENT_PREFIX = @"APListener";
#pragma mark - Activator Methods
-- (void)reloadActivatorListeners {
- NSLog(@"Reloading listeners!");
-
- if (!activatorListenersDict) {
- activatorListenersDict = [[NSMutableDictionary alloc] init];
- }
-
+- (void)reloadActivatorListeners:(NSDictionary*)listeners {
if (!activatorListenersArray) {
activatorListenersArray = [[NSMutableArray alloc] init];
}
- for (NSString *currKey in activatorListenersDict.allKeys) {
- NSString *identifier = activatorListenersDict[currKey];
+ for (APActivatorListener *currListener in activatorListenersArray) {
+ NSString *identifier = currListener.identifier;
NSString *eventName = [NSString stringWithFormat:@"%@%@", EVENT_PREFIX, identifier];
- NSLog(@"removed eventName = %@", eventName);
[LASharedActivator unregisterEventDataSourceWithEventName:eventName];
}
- [activatorListenersDict removeAllObjects];
[activatorListenersArray removeAllObjects];
- NSDictionary *pref = [NSDictionary dictionaryWithContentsOfFile:PREFERENCE_PATH];
- if ([pref objectForKey:@"activatorListeners"]) {
- for (NSDictionary *currListener in [pref objectForKey:@"activatorListeners"]) {
+ if ([listeners objectForKey:@"activatorListeners"]) {
+ for (NSDictionary *currListener in [listeners objectForKey:@"activatorListeners"]) {
NSString *trigger = currListener[@"trigger"];
BOOL isEnabled = [currListener[@"enabled"] boolValue];
if (trigger.length > 0 && isEnabled) {
- NSString *eventName = [NSString stringWithFormat:@"%@%@", EVENT_PREFIX, currListener[@"identifier"]];
- NSLog(@"Registered %@ for %@", eventName, trigger);
- [activatorListenersDict setObject:eventName forKey:[trigger lowercaseString]];
- [activatorListenersArray addObject:currListener];
+ APActivatorListener *newListener = [[APActivatorListener alloc] initWithDictionary:currListener];
+ NSString *eventName = [NSString stringWithFormat:@"%@%@", EVENT_PREFIX, newListener.identifier];
+ [activatorListenersArray addObject:newListener];
[LASharedActivator registerEventDataSource:self forEventName:eventName];
}
}
@@ -169,10 +133,10 @@ static NSString *EVENT_PREFIX = @"APListener";
}
- (NSString *)localizedTitleForEventName:(NSString *)eventName {
- for (NSDictionary *currListener in activatorListenersArray) {
- NSString *comp = [NSString stringWithFormat:@"%@%@", EVENT_PREFIX, currListener[@"identifier"]];
+ for (APActivatorListener *currListener in activatorListenersArray) {
+ NSString *comp = [NSString stringWithFormat:@"%@%@", EVENT_PREFIX, currListener.identifier];
if ([comp isEqualToString:eventName]) {
- return currListener[@"name"];
+ return currListener.name;
}
}
@@ -184,10 +148,10 @@ static NSString *EVENT_PREFIX = @"APListener";
}
- (NSString *)localizedDescriptionForEventName:(NSString *)eventName {
- for (NSDictionary *currListener in activatorListenersArray) {
- NSString *comp = [NSString stringWithFormat:@"%@%@", EVENT_PREFIX, currListener[@"identifier"]];
+ for (APActivatorListener *currListener in activatorListenersArray) {
+ NSString *comp = [NSString stringWithFormat:@"%@%@", EVENT_PREFIX, currListener.identifier];
if ([comp isEqualToString:eventName]) {
- return [NSString stringWithFormat:@"Siri Query - \"%@\"", currListener[@"trigger"]];
+ return [NSString stringWithFormat:@"Siri Query - \"%@\"", currListener.triggerString];
}
}
diff --git a/assistant+/assistantpluspluginmanager/APSession.xm b/assistant+/assistantpluspluginmanager/APSession.xm
index 5c5ba79..7fa9c2f 100644
--- a/assistant+/assistantpluspluginmanager/APSession.xm
+++ b/assistant+/assistantpluspluginmanager/APSession.xm
@@ -21,7 +21,6 @@ static NSMutableDictionary *sessionDict;
self.refId = [referenceId copy];
if (!self.refId) self.refId = [@"00000000-0000-0000-0000-000000000000" copy];
self.connection = connection;
- NSLog(@"Created a new session for request %@.", self.refId);
}
return self;
}
@@ -54,7 +53,6 @@ static NSMutableDictionary *sessionDict;
}
- (void)sendCustomSnippet:(NSString*)snippetClass withProperties:(NSDictionary*)props {
- NSLog(@"Sending snippet: %@", snippetClass);
[self sendAddViewsSnippet:snippetClass properties:props dialogPhase:@"Completion" scrollToTop:NO temporary:NO];
}
@@ -93,7 +91,6 @@ static NSMutableDictionary *sessionDict;
- (void)handleMessage:(NSString*)name withInfo:(NSDictionary*)locationData {
if (self.completionHandler) {
- NSLog(@"Sending %@" ,locationData);
self.completionHandler(locationData);
}
}
@@ -101,23 +98,26 @@ static NSMutableDictionary *sessionDict;
#pragma mark - AFConnection Communication
- (void)sendCommandToConnection:(NSDictionary*) dict {
- NSLog(@"Sending %@ to client", dict);
id ctx = nil;
id AceObject = objc_getClass("AceObject");
id BasicAceContext = objc_getClass("BasicAceContext");
if (!AceObject) NSLog(@"No AceObject class");
- if (!BasicAceContext) NSLog(@"AE ERROR: No BasicAceContext class");
+ if (!BasicAceContext) NSLog(@"No BasicAceContext class");
if (!dict) {
- NSLog(@"AE ERROR: SessionSendToClient: nil dict as an argument!");
return;
}
// create context
- if (ctx == nil) ctx = [[BasicAceContext alloc] init]; // ... is not needed normally, but just in case...
- if (!ctx) NSLog(@"AE ERROR: No context");
+ if (ctx == nil) {
+ ctx = [[BasicAceContext alloc] init]; // ... is not needed normally, but just in case...
+ }
+
+ if (!ctx) {
+ NSLog(@"Error getting BasicAceContext!");
+ }
if ([dict objectForKey:@"v"] && !s_ver) {
@@ -127,19 +127,15 @@ static NSMutableDictionary *sessionDict;
[(NSMutableDictionary*)dict setObject:s_ver forKey:@"v"];
}
- NSLog(@"AE: ###### ===> Sending Ace Object to Client: %@", dict);
-
// create real AceObject
id obj = [AceObject aceObjectWithDictionary:dict context:ctx];
if (obj == nil) {
- NSLog(@"AE ERROR: SessionSendToClient: NIL ACE OBJECT RETURNED FOR DICT: %@", dict);
return;
}
// call the original method to handle our new object
- if (self.connection == nil) { NSLog(@"AE: AFConnection is nil"); return; }
+ if (self.connection == nil) { NSLog(@"AP: AFConnection is nil"); return; }
- NSLog(@"Sending this: %@", obj);
if ([dict[@"$class"] isEqualToString:@"CommandSucceeded"]) {
[self.connection sendReplyCommand:obj];
} else {
@@ -149,7 +145,6 @@ static NSMutableDictionary *sessionDict;
-(void)sendAddViewsSnippet:(NSString*)snippetClass properties:(NSDictionary*)props dialogPhase:(NSString*)dialogPhase scrollToTop:(BOOL)scrollToTop temporary:(BOOL)temporary {
NSArray* views = [NSArray arrayWithObject:[self createSnippet:snippetClass properties:props]];
- // NSLog(@"About to send: %@", views);
[self sendAddViews:views];
}
@@ -160,7 +155,6 @@ static NSMutableDictionary *sessionDict;
NSMutableDictionary* lowLevelProps = [NSMutableDictionary dictionaryWithObjectsAndKeys:
props,@"snippetProps", snippetClass,@"snippetClass", nil];
- NSLog(@"Creating snippet: %@ with properties: %@", snippetClass, lowLevelProps);
return [self createObjectDictForGroup:@"zaid.assistantplus.plugin" class:@"SnippetObject" properties:lowLevelProps];
}
@@ -183,7 +177,6 @@ static NSMutableDictionary *sessionDict;
[objDict setObject:properties[currKey] forKey:currKey];
}
}
- NSLog(@"Returning: %@", objDict);
return objDict;
}
diff --git a/assistant+/assistantpluspluginmanager/APSpringboardUtils.m b/assistant+/assistantpluspluginmanager/APSpringboardUtils.m
index bef1d70..1a7a48e 100644
--- a/assistant+/assistantpluspluginmanager/APSpringboardUtils.m
+++ b/assistant+/assistantpluspluginmanager/APSpringboardUtils.m
@@ -22,19 +22,21 @@
NSDictionary *currLocation;
}
+static const char *root_helper_path = "/Applications/AssistantPlusApp.app/assistantplus_root_helper";
+
+ (id)sharedAPUtils {
static APSpringboardUtils *sharedObj = nil;
@synchronized(self) {
if (sharedObj == nil) {
- NSLog(@"CREATED SHARED UTILS!");
sharedObj = [[self alloc] init];
[sharedObj loadPlugins];
CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.springboard"];
[center runServerOnCurrentThread];
[center registerForMessageName:@"RetrievedLocation" target:sharedObj selector:@selector(gotCurrentLocation:withInfo:)];
- [center registerForMessageName:@"UpdateActivatorListeners" target:sharedObj selector:@selector(updateActivatorListeners)];
+ [center registerForMessageName:@"UpdateActivatorListeners" target:sharedObj selector:@selector(updateActivatorListeners:withListeners:)];
[center registerForMessageName:@"UpdateCustomReplies" target:sharedObj selector:@selector(updateCustomReplies:withReplies:)];
[center registerForMessageName:@"respringForListeners" target:sharedObj selector:@selector(respring)];
+ [center registerForMessageName:@"getInstalledPlugins" target:sharedObj selector:@selector(getInstalledPlugins:withInfo:)];
}
}
return sharedObj;
@@ -49,18 +51,15 @@
NSLog(@"APSpringboardUtils: Loaded Plugin Manager: %@", pluginManager);
}
-- (void)updateActivatorListeners {
- NSLog(@"Calling1 on %@", pluginManager);
- [pluginManager reloadActivatorListeners];
+- (void)updateActivatorListeners:(NSString*)msg withListeners:(NSDictionary*)listeners {
+ [pluginManager reloadActivatorListeners:listeners];
}
- (void)updateCustomReplies:(NSString*)msg withReplies:(NSDictionary*)dict {
- NSLog(@"AP SB: Updating custom replies with %@", dict);
[pluginManager reloadCustomRepliesPlugin:dict];
}
- (void)respring {
- NSLog(@"Respringing here!");
pid_t pid;
int status;
const char *argv[] = {"killall", "backboardd", NULL};
@@ -68,22 +67,33 @@
waitpid(pid, &status, WEXITED);
}
+- (NSDictionary*)getInstalledPlugins:(NSString*)msg withInfo:(NSDictionary*)info {
+ return [pluginManager getInstalledPlugins];
+}
+
- (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *info))completion {
+ [self startLocationDaemon];
self.completionHandler = completion;
CPDistributedMessagingCenter* center = [CPDistributedMessagingCenter centerNamed:@"com.zaid.applus.daemon"];
[center sendMessageName:@"RetrieveLocation" userInfo:nil];
}
- (void)gotCurrentLocation:(NSString*)msg withInfo:(NSDictionary*)info {
- NSLog(@"APSU got: %@", info);
if (info) {
NSDictionary *locInfo = info[@"Location"];
if (locInfo && self.completionHandler) {
- NSLog(@"Sending: %@ to block!", locInfo);
self.completionHandler(locInfo);
self.completionHandler = nil;
}
}
+ [self stopLocationDaemon];
}
+- (void)startLocationDaemon {
+ system("/Applications/AssistantPlusApp.app/assistantplus_root_helper start");
+}
+
+- (void)stopLocationDaemon {
+ system("/Applications/AssistantPlusApp.app/assistantplus_root_helper stop");
+}
@end
diff --git a/assistant+/assistantpluspluginmanager/AssistantHeaders.h b/assistant+/assistantpluspluginmanager/AssistantHeaders.h
index 5af94f8..1619e76 100644
--- a/assistant+/assistantpluspluginmanager/AssistantHeaders.h
+++ b/assistant+/assistantpluspluginmanager/AssistantHeaders.h
@@ -108,6 +108,11 @@ typedef SOObject SOAceObject;
- (void)sendReplyCommand:(id)arg1;
- (void)_willCompleteRequest;
- (void)_tellDelegateRequestFinished;
+- (void)cancelRequest;
+- (void)_requestDidEnd;
+- (void)endSession;
+- (void)clearContext;
+-(void)_cancelRequestTimeout;
@end
@interface SABaseCommand : AceObject
@@ -115,11 +120,10 @@ typedef SOObject SOAceObject;
@property(copy, nonatomic) NSString *aceId;
@end
-@interface AFConnectionClientServiceDelegate : NSObject
-{
+@interface AFConnectionClientServiceDelegate : NSObject {
AFConnection *_connection;
}
-
+- (void)requestDidFinish;
@end
@interface SABaseClientBoundCommand : SABaseCommand
@@ -162,8 +166,33 @@ typedef SOObject SOAceObject;
@property(copy, nonatomic) NSArray *phrases;
@end
+@interface AFUserUtterance : NSObject {
+
+ NSMutableArray* _phrases;
+ NSMutableArray* _tokens;
+ NSString* _text;
+ NSDictionary* _correctionIdentifier;
+
+}
+
+@property (nonatomic,readonly) NSDictionary * correctionIdentifier; //@synthesize correctionIdentifier=_correctionIdentifier - In the implementation block
+@property (nonatomic,readonly) NSArray * dictationResult;
+-(id)description;
+-(id)bestTextInterpretation;
+-(id)initWithPhrases:(id)arg1 correctionIdentifier:(id)arg2 ;
+-(id)initWithTokens:(id)arg1 correctionIdentifier:(id)arg2 ;
+-(id)initWithString:(id)arg1 correctionIdentifier:(id)arg2 ;
+-(NSArray *)dictationResult;
+-(id)streamingTokens;
+-(NSDictionary *)correctionIdentifier;
+@end
+
+
@interface SASSpeechRecognized : SABaseClientBoundCommand
@property(retain, nonatomic) SASRecognition *recognition;
+- (id)af_bestTextInterpretation;
+- (id)af_correctionContext;
+- (AFUserUtterance*)af_userUtteranceValue;
@end
@interface AFSpeechToken : NSObject
diff --git a/assistant+/assistantpluspluginmanager/AssistantPlusHeaders.h b/assistant+/assistantpluspluginmanager/AssistantPlusHeaders.h
index 658fee7..4408c08 100644
--- a/assistant+/assistantpluspluginmanager/AssistantPlusHeaders.h
+++ b/assistant+/assistantpluspluginmanager/AssistantPlusHeaders.h
@@ -18,7 +18,6 @@
-(NSMutableDictionary*)createTextSnippet:(NSString*)text;
- (void)sendCustomSnippet:(NSString*)snippetClass withProperties:(NSDictionary*)props;
- (void)sendRequestCompleted;
--(NSMutableDictionary*)createAssistantUtteranceView:(NSString*)text;
- (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *info))completion;
@end
@@ -31,7 +30,7 @@
@required
+(id)sharedManager;
- (void)reloadCustomRepliesPlugin:(NSDictionary*)replies;
-- (void)reloadActivatorListeners;
+- (void)reloadActivatorListeners:(NSDictionary*)listeners;
@end
@protocol APPluginManager
@@ -44,12 +43,7 @@
@protocol APPluginSnippet
@optional
-/// Initializes a snippet by properties
-(id)initWithProperties:(NSDictionary*)props;
-/// Initializes a snippet by properties and system
--(id)initWithProperties:(NSDictionary*)props system:(id)system;
-/// Returns a view representing snippet, can be self if the conforming class is already UIView
-
@end
@interface APPluginSnippetViewController : UIViewController
@@ -57,30 +51,12 @@
@end
-/** Protocol specifying methods of an extension class handling commands.
- Classes conforming to this protocol are initialized just after loading bundle and will remain in memory.
- Don't forget you really should prefix your class with some shortcut, e.g. K3AAwesomeCommand!
- */
@protocol APPluginCommand
@optional
-
-(BOOL)handleSpeech:(NSString*)text withTokens:(NSSet*)tokens withSession:(id)session;
--(id)initWithSystem:(id)manager;
-
--(void)assistantDismissed;
-
@end
-
-/// Protocol specifying methods of the extension's principal class
@protocol APPlugin
-
@required
-/// The first method which is called on your class, system is where you register commands and snippets
--(id)initWithSystem:(id)system;
-
-@optional
-
--(void)assistantDismissed; //clean up
-
+-(id)initWithPluginManager:(id)system;
@end
diff --git a/assistant+/assistantpluspluginmanager/Makefile b/assistant+/assistantpluspluginmanager/Makefile
index 84e6e61..12a42aa 100644
--- a/assistant+/assistantpluspluginmanager/Makefile
+++ b/assistant+/assistantpluspluginmanager/Makefile
@@ -1,11 +1,15 @@
include theos/makefiles/common.mk
+export GO_EASY_ON_ME=1
+export TARGET_IPHONEOS_DEPLOYMENT_VERSION = 8.0
+
TWEAK_NAME = AssistantPlusPluginManager
AssistantPlusPluginManager_CFLAGS = -fobjc-arc
-AssistantPlusPluginManager_FILES = Tweak.xm APPluginSystem.m APPlugin.m APSpringboardUtils.m
+AssistantPlusPluginManager_FILES = Tweak.xm APPluginSystem.m APPlugin.m APSpringboardUtils.m APActivatorListener.m
AssistantPlusPluginManager_PRIVATE_FRAMEWORKS = AssistantServices SAObjects AppSupport
AssistantPlusPluginManager_FRAMEWORKS = Foundation UIKit CoreLocation
AssistantPlusPluginManager_LIBRARIES = substrate activator
+AssistantPlusPluginManager_FLAGS = -Sentitlements.xml
include $(THEOS_MAKE_PATH)/tweak.mk
diff --git a/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/contents.xcworkspacedata b/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/contents.xcworkspacedata
index 35494f0..6d9b4f7 100644
--- a/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/contents.xcworkspacedata
+++ b/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/contents.xcworkspacedata
@@ -1,6 +1,12 @@
+
+
+
+
diff --git a/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/assistant+/assistantpluspluginmanager/SBAPPluginManager.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate
index 1bfce6b..bc98b56 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/Tweak.xm b/assistant+/assistantpluspluginmanager/Tweak.xm
index 6a56a6f..c14bb60 100644
--- a/assistant+/assistantpluspluginmanager/Tweak.xm
+++ b/assistant+/assistantpluspluginmanager/Tweak.xm
@@ -10,5 +10,4 @@ static NSDictionary *currLocation;
static inline __attribute__((constructor)) void init() {
NSLog(@"Creating AssistantPlusSBPluginManager!");
sharedUtils = [APSpringboardUtils sharedAPUtils];
- NSLog(@"FUCK IS: %@", sharedUtils);
}
\ No newline at end of file
diff --git a/assistant+/assistantpluspluginmanager/entitlements.xml b/assistant+/assistantpluspluginmanager/entitlements.xml
new file mode 100644
index 0000000..5c5c43c
--- /dev/null
+++ b/assistant+/assistantpluspluginmanager/entitlements.xml
@@ -0,0 +1,7 @@
+
+
+
+ com.apple.locationd.preauthorized
+
+
+
\ No newline at end of file
diff --git a/assistant+/assistantpluspluginmanager/obj/AssistantPlusPluginManager.dylib b/assistant+/assistantpluspluginmanager/obj/AssistantPlusPluginManager.dylib
index 698a695..fe14b22 100755
Binary files a/assistant+/assistantpluspluginmanager/obj/AssistantPlusPluginManager.dylib and b/assistant+/assistantpluspluginmanager/obj/AssistantPlusPluginManager.dylib differ
diff --git a/assistant+/customreply/.theos/fakeroot b/assistant+/customreply/.theos/fakeroot
new file mode 100644
index 0000000..e69de29
diff --git a/assistant+/customreply/.theos/packages/com.assistantplus.customreply-0.0.1 b/assistant+/customreply/.theos/packages/com.assistantplus.customreply-0.0.1
new file mode 100644
index 0000000..8fdd954
--- /dev/null
+++ b/assistant+/customreply/.theos/packages/com.assistantplus.customreply-0.0.1
@@ -0,0 +1 @@
+22
\ No newline at end of file
diff --git a/assistant+/customreply/AssistantPlusHeaders.h b/assistant+/customreply/AssistantPlusHeaders.h
new file mode 100644
index 0000000..0e73d5c
--- /dev/null
+++ b/assistant+/customreply/AssistantPlusHeaders.h
@@ -0,0 +1,99 @@
+//
+// AssistantPlusHeaders.h
+// For use with Assistant+ v1.0
+//
+// Created by Zaid Elkurdi on 2/28/15.
+//
+//
+
+#ifndef _AssistantPlusHeaders_h
+#define _AssistantPlusHeaders_h
+
+// Represents the current Siri session
+@protocol APSiriSession
+/* Send a simple text snippet that Siri will read.
+
+ temporary: If true then the snippet will be replaced by the next view sent to the session.
+ For this to function properly the dialogPhase should be "Reflection"
+ scrollToTop: If true the Siri UI will be scrolled down so that the new view is at the top.
+ dialogPhase: Possible values are Completion, Reflection, Summary, Error, Clarification, and Acknowledgement */
+- (void)sendTextSnippet:(NSString*)text temporary:(BOOL)temporary scrollToTop:(BOOL)toTop dialogPhase:(NSString*)phase;
+
+/* Create an editable dictionary representing a text snippet. In order to send this
+ to the user you must add it to an NSArray and use sendAddviews: */
+-(NSMutableDictionary*)createTextSnippet:(NSString*)text;
+
+/* Send several views to the user at once */
+- (void)sendAddViews:(NSArray*)views;
+
+/* Send several views to the user with control over the parameters */
+- (void)sendAddViews:(NSArray*)views dialogPhase:(NSString*)dialogPhase scrollToTop:(BOOL)toTop temporary:(BOOL)temporary;
+
+/* Create and immediately send a custom snippet with the specified properties. The snippet must
+ have been registered with the plugin manager */
+- (void)sendCustomSnippet:(NSString*)snippetClass withProperties:(NSDictionary*)props;
+
+/* Create an editable dictionary representing a custom snippet. In order to send this
+ to the user you must add it to an NSArray and use sendAddViews: */
+-(NSMutableDictionary*)createSnippet:(NSString*)snippetClass properties:(NSDictionary*)props;
+
+/* Tell the session that you're done with this request and that it can end. You must do
+ this or Siri will timeout and display an error message.*/
+- (void)sendRequestCompleted;
+
+/* Retrieve the user's current location and then execute the code in the completion block
+ with the location info. The location info will be structured as follows:
+
+ NSDictionary *dict = @{@"latitude" : NSNumber,
+ @"longitude" : NSNumber,
+ @"horizontalAccuracy" : NSNumber,
+ @"verticalAccuracy" : NSNumber,
+ @"speed" : NSNumber,
+ @"course" : NSNumber,
+ @"timestamp" : NSDate} */
+- (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *locationInfo))completion;
+
+@end
+
+/* None of your classes should need to conform to this protocol, but you will use it to register
+ your command and snippet classes when your principal class's (the one that conforms to APPlugin)
+ initWithSystem: method is called */
+@protocol APPluginManager
+@required
+-(BOOL)registerCommand:(Class)commandClass;
+-(BOOL)registerSnippet:(Class)snippetClass;
+@end
+
+/* Your custom snippet class (which should always be a subclass of UIViewController)
+ will need to conform to this protocol */
+@protocol APPluginSnippet
+@optional
+/* If you use APSiriSession's sendCustomSnippet:withProperties: and want to be able
+ to access the properties in your snippet then you must implement this method. If you
+ don't implement this method then the snippet will be created with [yourSnippet init] */
+-(id)initWithProperties:(NSDictionary*)props;
+
+@end
+
+/* This is where you will handle the user's query and determine if your plugin
+ should handle it. This "command" class should essentially be the brain of your plugin
+ and determine which snippet/s to show or action/s to take */
+@protocol APPluginCommand
+@required
+/* You should try to make this method run as quickly as possible, as this method will be called on
+ all installed plugins or until one returns YES. If you've determined that your plugin should handle
+ the user's query then do any time-intensive tasks (such as network calls) on another thread */
+-(BOOL)handleSpeech:(NSString*)text withTokens:(NSSet*)tokens withSession:(id)session;
+@end
+
+
+/* Your principal/"main" class should conform to this protocol. */
+@protocol APPlugin
+@required
+/* When your plugin is initialized the plugin manager will call this method. This is
+ when you should register you command/s and snippet/s using the system's registerCommand: and
+ registerSnippet: methods, repsectively. */
+-(id)initWithPluginManager:(id)manager;
+@end
+
+#endif
diff --git a/assistant+/customreply/Makefile b/assistant+/customreply/Makefile
new file mode 100644
index 0000000..9170862
--- /dev/null
+++ b/assistant+/customreply/Makefile
@@ -0,0 +1,16 @@
+include theos/makefiles/common.mk
+
+export ARCHS = armv7 arm64
+export TARGET = iphone:clang:latest:8.0
+export SDKVERSION=8.1
+
+BUNDLE_NAME = customreply
+customreply_BUNDLE_EXTENSION = assistantPlugin
+customreply_FILES = customReplyCommands.m customreply.m
+customreply_INSTALL_PATH = /Library/AssistantPlusPlugins/
+customreply_CFLAGS = -fobjc-arc
+
+include $(THEOS_MAKE_PATH)/bundle.mk
+
+after-install::
+ install.exec "killall -9 SpringBoard"
diff --git a/assistant+/customreply/Resources/Info.plist b/assistant+/customreply/Resources/Info.plist
new file mode 100644
index 0000000..0e1a75f
--- /dev/null
+++ b/assistant+/customreply/Resources/Info.plist
@@ -0,0 +1,41 @@
+
+
+
+
+ CFBundleName
+ CustomReply
+ APPluginName
+ Custom Replies
+ APPluginAuthor
+ Zaid Elkurdi
+ CFBundleIdentifier
+ com.assistantplus.customreplyidentifier
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleVersion
+ 1
+ CFBundleDisplayName
+ CustomReply
+ MinimumOSVersion
+ 5.0
+ CFBundleSupportedPlatforms
+
+ iPhoneOS
+
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ AppBundleID
+ com.assistantplus.customreplyidentifier
+ UIDeviceFamily
+
+ 1
+ 2
+
+ CFBundleShortVersionString
+ 1.0
+ NSPrincipalClass
+ customreply
+
+
diff --git a/assistant+/customreply/Tweak.xm b/assistant+/customreply/Tweak.xm
new file mode 100644
index 0000000..dc9c3a8
--- /dev/null
+++ b/assistant+/customreply/Tweak.xm
@@ -0,0 +1,34 @@
+/* How to Hook with Logos
+Hooks are written with syntax similar to that of an Objective-C @implementation.
+You don't need to #include , it will be done automatically, as will
+the generation of a class list and an automatic constructor.
+
+%hook ClassName
+
+// Hooking a class method
++ (id)sharedInstance {
+ return %orig;
+}
+
+// Hooking an instance method with an argument.
+- (void)messageName:(int)argument {
+ %log; // Write a message about this call, including its class, name and arguments, to the system log.
+
+ %orig; // Call through to the original function with its original arguments.
+ %orig(nil); // Call through to the original function with a custom argument.
+
+ // If you use %orig(), you MUST supply all arguments (except for self and _cmd, the automatically generated ones.)
+}
+
+// Hooking an instance method with no arguments.
+- (id)noArguments {
+ %log;
+ id awesome = %orig;
+ [awesome doSomethingElse];
+
+ return awesome;
+}
+
+// Always make sure you clean up after yourself; Not doing so could have grave consequences!
+%end
+*/
diff --git a/assistant+/customreply/control b/assistant+/customreply/control
new file mode 100644
index 0000000..593c5c8
--- /dev/null
+++ b/assistant+/customreply/control
@@ -0,0 +1,9 @@
+Package: com.assistantplus.customreply
+Name: customreply
+Depends: mobilesubstrate
+Version: 0.0.1
+Architecture: iphoneos-arm
+Description: An awesome MobileSubstrate tweak!
+Maintainer: Zaid Elkurdi
+Author: Zaid Elkurdi
+Section: Tweaks
diff --git a/assistant+/customreply/customReplyCommands.h b/assistant+/customreply/customReplyCommands.h
new file mode 100644
index 0000000..ce3f04c
--- /dev/null
+++ b/assistant+/customreply/customReplyCommands.h
@@ -0,0 +1,14 @@
+//
+// customReplyCommands.h
+// customreply
+//
+// Created by Zaid Elkurdi on 3/22/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import
+#import "AssistantPlusHeaders.h"
+
+@interface customReplyCommands : NSObject
+-(BOOL)handleSpeech:(NSString *)text withTokens:(NSSet *)tokens withSession:(id)session;
+@end
diff --git a/assistant+/customreply/customReplyCommands.m b/assistant+/customreply/customReplyCommands.m
new file mode 100644
index 0000000..f00b635
--- /dev/null
+++ b/assistant+/customreply/customReplyCommands.m
@@ -0,0 +1,55 @@
+//
+// customReplyCommands.m
+// customreply
+//
+// Created by Zaid Elkurdi on 3/22/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import "customReplyCommands.h"
+#define kPreferencesPath "/var/mobile/Library/Preferences/com.assistantplus.app.plist"
+
+@implementation customReplyCommands {
+ NSDictionary *phrases;
+}
+
+- (void)createPhraseDictionary:(NSDictionary*)repliesDict {
+ NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
+ NSArray *customReplies = repliesDict[@"customReplies"];
+ if (customReplies) {
+ for (NSDictionary *currReply in customReplies) {
+ NSString *currTrigger = currReply[@"trigger"];
+ NSString *currResponse = currReply[@"response"];
+ if ([dict objectForKey:[currTrigger lowercaseString]]) {
+ NSMutableArray *mutableCmds = [[dict objectForKey:[currTrigger lowercaseString]] mutableCopy];
+ [mutableCmds addObject:currResponse];
+ [dict setObject:mutableCmds forKey:[currTrigger lowercaseString]];
+ } else {
+ [dict setObject:@[currResponse] forKey:[currTrigger lowercaseString]];
+ }
+ }
+ }
+ phrases = dict;
+}
+
+-(BOOL)handleSpeech:(NSString *)text withTokens:(NSSet *)tokens withSession:(id)session {
+ if (!phrases) {
+ NSDictionary *preferences = [NSDictionary dictionaryWithContentsOfFile:@kPreferencesPath];
+ [self createPhraseDictionary:preferences];
+ }
+
+ text = [[text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] lowercaseString];
+
+ if ([phrases objectForKey:text]) {
+ NSArray *customReplies = [phrases objectForKey:text];
+ for (NSString *currReply in customReplies) {
+ [session sendTextSnippet:currReply temporary:NO scrollToTop:YES dialogPhase:@"Completion"];
+ }
+ [session sendRequestCompleted];
+ return YES;
+ }
+
+ return NO;
+}
+
+@end
diff --git a/assistant+/customreply/customreply.h b/assistant+/customreply/customreply.h
new file mode 100644
index 0000000..b1bb4a7
--- /dev/null
+++ b/assistant+/customreply/customreply.h
@@ -0,0 +1,14 @@
+//
+// customreply.h
+// customreply
+//
+// Created by Zaid Elkurdi on 3/21/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import
+#import "AssistantPlusHeaders.h"
+
+@interface customreply : NSObject
+-(id)initWithPluginManager:(id)manager;
+@end
diff --git a/assistant+/customreply/customreply.m b/assistant+/customreply/customreply.m
new file mode 100644
index 0000000..b597311
--- /dev/null
+++ b/assistant+/customreply/customreply.m
@@ -0,0 +1,23 @@
+//
+// customreply.m
+// customreply
+//
+// Created by Zaid Elkurdi on 3/21/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import "customreply.h"
+#import "customReplyCommands.h"
+
+@implementation customreply
+
+-(id)initWithPluginManager:(id)manager {
+ if (self = [super init]) {
+ if (manager) {
+ [manager registerCommand:[customReplyCommands class]];
+ }
+ }
+ return self;
+}
+
+@end
diff --git a/assistant+/customreply/customreply.plist b/assistant+/customreply/customreply.plist
new file mode 100644
index 0000000..10dc654
--- /dev/null
+++ b/assistant+/customreply/customreply.plist
@@ -0,0 +1 @@
+{ Filter = { Bundles = ( "com.apple.springboard" ); }; }
diff --git a/assistant+/customreply/obj/.stamp b/assistant+/customreply/obj/.stamp
new file mode 100644
index 0000000..e69de29
diff --git a/assistant+/customreply/obj/customreply.assistantPlugin/Info.plist b/assistant+/customreply/obj/customreply.assistantPlugin/Info.plist
new file mode 100644
index 0000000..0e1a75f
--- /dev/null
+++ b/assistant+/customreply/obj/customreply.assistantPlugin/Info.plist
@@ -0,0 +1,41 @@
+
+
+
+
+ CFBundleName
+ CustomReply
+ APPluginName
+ Custom Replies
+ APPluginAuthor
+ Zaid Elkurdi
+ CFBundleIdentifier
+ com.assistantplus.customreplyidentifier
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleVersion
+ 1
+ CFBundleDisplayName
+ CustomReply
+ MinimumOSVersion
+ 5.0
+ CFBundleSupportedPlatforms
+
+ iPhoneOS
+
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ AppBundleID
+ com.assistantplus.customreplyidentifier
+ UIDeviceFamily
+
+ 1
+ 2
+
+ CFBundleShortVersionString
+ 1.0
+ NSPrincipalClass
+ customreply
+
+
diff --git a/assistant+/customreply/obj/customreply.assistantPlugin/customreply b/assistant+/customreply/obj/customreply.assistantPlugin/customreply
new file mode 100755
index 0000000..dbfeed5
Binary files /dev/null and b/assistant+/customreply/obj/customreply.assistantPlugin/customreply differ
diff --git a/assistant+/customreply/theos b/assistant+/customreply/theos
new file mode 120000
index 0000000..e30945d
--- /dev/null
+++ b/assistant+/customreply/theos
@@ -0,0 +1 @@
+/opt/theos
\ No newline at end of file
diff --git a/assistant+/layout/DEBIAN/control b/assistant+/layout/DEBIAN/control
index db5aa33..3735f59 100644
--- a/assistant+/layout/DEBIAN/control
+++ b/assistant+/layout/DEBIAN/control
@@ -1,9 +1,10 @@
Package: com.zaid.assistant+
Name: Assistant+
-Depends: mobilesubstrate
-Version: 0.0.1
+Depends: libactivator (>= 1.8.3), mobilesubstrate
+Version: 1.0.0
Architecture: iphoneos-arm
-Description: Framework for Siri Extensions
+Description: Assign commands to trigger Activator events from Siri, set up custom replies for Siri, and also use and create plugins for Siri using the Assistant+ plugin framework
Maintainer: Zaid Elkurdi
Author: Zaid Elkurdi
Section: Tweaks
+Icon: file:///Applications/AssistantPlusApp.app/Icon.png
diff --git a/assistant+/layout/DEBIAN/postinst b/assistant+/layout/DEBIAN/postinst
index 4f067be..03d7c60 100755
--- a/assistant+/layout/DEBIAN/postinst
+++ b/assistant+/layout/DEBIAN/postinst
@@ -1,6 +1,11 @@
#!/bin/sh
chown root:wheel /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
-launchctl unload /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
+launchctl unload /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist 2> /dev/null
launchctl load /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
-echo "Starting APLocationManger..."
-exit 0
+
+chown root:wheel /Applications/AssistantPlusApp.app/assistantplus_root_helper
+chmod 6755 /Applications/AssistantPlusApp.app/assistantplus_root_helper
+
+uicache
+
+exit 0
\ No newline at end of file
diff --git a/assistant+/layout/DEBIAN/preinst b/assistant+/layout/DEBIAN/preinst
deleted file mode 100755
index 97b91c0..0000000
--- a/assistant+/layout/DEBIAN/preinst
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-echo "Installing APLocationManager!"
-chown root:wheel /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
-launchctl unload /Library/LaunchDaemons/com.zaid.aplocationdaemon.plist
-exit 0
diff --git a/assistant+/obj/Assistant+.dylib b/assistant+/obj/Assistant+.dylib
index 4cf52a6..791fe6d 100755
Binary files a/assistant+/obj/Assistant+.dylib and b/assistant+/obj/Assistant+.dylib differ
diff --git a/customreply/customreply.xcodeproj/project.pbxproj b/customreply/customreply.xcodeproj/project.pbxproj
index c51f493..bf0bc4d 100644
--- a/customreply/customreply.xcodeproj/project.pbxproj
+++ b/customreply/customreply.xcodeproj/project.pbxproj
@@ -29,7 +29,6 @@
B8315F0C1ABE856000818E61 /* customreplyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = customreplyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B8315F121ABE856000818E61 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
B8315F1D1ABE862A00818E61 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = customreply/Makefile; sourceTree = ""; };
- B8315F241ABF466900818E61 /* AssistantHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AssistantHeaders.h; sourceTree = ""; };
B8315F251ABF466900818E61 /* AssistantPlusHeaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AssistantPlusHeaders.h; sourceTree = ""; };
B8315F261ABF480000818E61 /* customReplyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = customReplyCommands.h; sourceTree = ""; };
B8315F271ABF480000818E61 /* customReplyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = customReplyCommands.m; sourceTree = ""; };
@@ -69,7 +68,6 @@
B8315F031ABE856000818E61 /* customreply */ = {
isa = PBXGroup;
children = (
- B8315F241ABF466900818E61 /* AssistantHeaders.h */,
B8315F251ABF466900818E61 /* AssistantPlusHeaders.h */,
B8315F261ABF480000818E61 /* customReplyCommands.h */,
B8315F271ABF480000818E61 /* customReplyCommands.m */,
@@ -416,6 +414,7 @@
B8315F171ABE856000818E61 /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
B8315F181ABE856000818E61 /* Build configuration list for PBXNativeTarget "customreplyTests" */ = {
isa = XCConfigurationList;
@@ -424,6 +423,7 @@
B8315F1A1ABE856000818E61 /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
B8315F201ABE867400818E61 /* Build configuration list for PBXLegacyTarget "customreplyTheos" */ = {
isa = XCConfigurationList;
@@ -432,6 +432,7 @@
B8315F221ABE867400818E61 /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
diff --git a/customreply/customreply.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate b/customreply/customreply.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate
index 7ef79e7..786f268 100644
Binary files a/customreply/customreply.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate and b/customreply/customreply.xcodeproj/project.xcworkspace/xcuserdata/Zaid.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/customreply/customreply/.theos/packages/com.assistantplus.customreply-0.0.1 b/customreply/customreply/.theos/packages/com.assistantplus.customreply-0.0.1
index 8fdd954..b393560 100644
--- a/customreply/customreply/.theos/packages/com.assistantplus.customreply-0.0.1
+++ b/customreply/customreply/.theos/packages/com.assistantplus.customreply-0.0.1
@@ -1 +1 @@
-22
\ No newline at end of file
+23
\ No newline at end of file
diff --git a/customreply/customreply/AssistantPlusHeaders.h b/customreply/customreply/AssistantPlusHeaders.h
index 8e7fdd9..0e73d5c 100644
--- a/customreply/customreply/AssistantPlusHeaders.h
+++ b/customreply/customreply/AssistantPlusHeaders.h
@@ -1,88 +1,99 @@
//
// AssistantPlusHeaders.h
-//
+// For use with Assistant+ v1.0
//
-// Created by Zaid Elkurdi on 3/12/15.
+// Created by Zaid Elkurdi on 2/28/15.
//
//
#ifndef _AssistantPlusHeaders_h
#define _AssistantPlusHeaders_h
+// Represents the current Siri session
@protocol APSiriSession
+/* Send a simple text snippet that Siri will read.
+
+ temporary: If true then the snippet will be replaced by the next view sent to the session.
+ For this to function properly the dialogPhase should be "Reflection"
+ scrollToTop: If true the Siri UI will be scrolled down so that the new view is at the top.
+ dialogPhase: Possible values are Completion, Reflection, Summary, Error, Clarification, and Acknowledgement */
- (void)sendTextSnippet:(NSString*)text temporary:(BOOL)temporary scrollToTop:(BOOL)toTop dialogPhase:(NSString*)phase;
+
+/* Create an editable dictionary representing a text snippet. In order to send this
+ to the user you must add it to an NSArray and use sendAddviews: */
-(NSMutableDictionary*)createTextSnippet:(NSString*)text;
+
+/* Send several views to the user at once */
- (void)sendAddViews:(NSArray*)views;
+
+/* Send several views to the user with control over the parameters */
- (void)sendAddViews:(NSArray*)views dialogPhase:(NSString*)dialogPhase scrollToTop:(BOOL)toTop temporary:(BOOL)temporary;
--(NSMutableDictionary*)createSnippet:(NSString*)snippetClass properties:(NSDictionary*)props;
+
+/* Create and immediately send a custom snippet with the specified properties. The snippet must
+ have been registered with the plugin manager */
- (void)sendCustomSnippet:(NSString*)snippetClass withProperties:(NSDictionary*)props;
+
+/* Create an editable dictionary representing a custom snippet. In order to send this
+ to the user you must add it to an NSArray and use sendAddViews: */
+-(NSMutableDictionary*)createSnippet:(NSString*)snippetClass properties:(NSDictionary*)props;
+
+/* Tell the session that you're done with this request and that it can end. You must do
+ this or Siri will timeout and display an error message.*/
- (void)sendRequestCompleted;
--(NSMutableDictionary*)createAssistantUtteranceView:(NSString*)text;
-- (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *info))completion;
+
+/* Retrieve the user's current location and then execute the code in the completion block
+ with the location info. The location info will be structured as follows:
+
+ NSDictionary *dict = @{@"latitude" : NSNumber,
+ @"longitude" : NSNumber,
+ @"horizontalAccuracy" : NSNumber,
+ @"verticalAccuracy" : NSNumber,
+ @"speed" : NSNumber,
+ @"course" : NSNumber,
+ @"timestamp" : NSDate} */
+- (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *locationInfo))completion;
+
@end
+/* None of your classes should need to conform to this protocol, but you will use it to register
+ your command and snippet classes when your principal class's (the one that conforms to APPlugin)
+ initWithSystem: method is called */
@protocol APPluginManager
@required
-/// Register a command class
--(BOOL)registerCommand:(Class)cls;
-/// Register a snippet class
--(BOOL)registerSnippet:(Class)cls;
+-(BOOL)registerCommand:(Class)commandClass;
+-(BOOL)registerSnippet:(Class)snippetClass;
@end
-@interface AceObject : NSObject
-@property(copy, nonatomic) NSString *refId;
-@property(copy, nonatomic) NSString *aceId;
-+ (id)aceObjectWithDictionary:(id)arg1 context:(id)arg2;
-@end
-
-@protocol SiriUIViewController
-@property (nonatomic,retain) AceObject * aceObject;
+/* Your custom snippet class (which should always be a subclass of UIViewController)
+ will need to conform to this protocol */
+@protocol APPluginSnippet
@optional
--(double)desiredHeightForWidth:(double)arg1;
--(double)desiredHeight;
--(id)navigationTitle;
--(void)transcriptViewControllerTappedOutsideEditingView;
-
-@required
--(void)siriWillActivateFromSource:(long long)arg1;
--(void)siriDidDeactivate;
--(void)wasAddedToTranscript;
--(AceObject *)aceObject;
--(void)setAceObject:(AceObject*)arg1;
-@end
-
-@protocol APPluginSnippet
-@required
--(double)desiredHeightForWidth:(double)height;
-@optional
-/// Initializes a snippet by properties
+/* If you use APSiriSession's sendCustomSnippet:withProperties: and want to be able
+ to access the properties in your snippet then you must implement this method. If you
+ don't implement this method then the snippet will be created with [yourSnippet init] */
-(id)initWithProperties:(NSDictionary*)props;
-/// Initializes a snippet by properties and system
--(id)initWithProperties:(NSDictionary*)props system:(id)system;
-/// Returns a view representing snippet, can be self if the conforming class is already UIView
+
@end
-
-/** Protocol specifying methods of an extension class handling commands.
- Classes conforming to this protocol are initialized just after loading bundle and will remain in memory.
- Don't forget you really should prefix your class with some shortcut, e.g. K3AAwesomeCommand!
- */
+/* This is where you will handle the user's query and determine if your plugin
+ should handle it. This "command" class should essentially be the brain of your plugin
+ and determine which snippet/s to show or action/s to take */
@protocol APPluginCommand
@required
+/* You should try to make this method run as quickly as possible, as this method will be called on
+ all installed plugins or until one returns YES. If you've determined that your plugin should handle
+ the user's query then do any time-intensive tasks (such as network calls) on another thread */
-(BOOL)handleSpeech:(NSString*)text withTokens:(NSSet*)tokens withSession:(id)session;
-@optional
--(id)initWithSystem:(id)manager;
--(void)assistantDismissed;
@end
-/// Protocol specifying methods of the extension's principal class
+/* Your principal/"main" class should conform to this protocol. */
@protocol APPlugin
@required
-/// The first method which is called on your class, system is where you register commands and snippets
--(id)initWithSystem:(id)system;
-@optional
--(void)assistantDismissed; //clean up
+/* When your plugin is initialized the plugin manager will call this method. This is
+ when you should register you command/s and snippet/s using the system's registerCommand: and
+ registerSnippet: methods, repsectively. */
+-(id)initWithPluginManager:(id)manager;
@end
#endif
diff --git a/customreply/customreply/Makefile b/customreply/customreply/Makefile
index cb58181..9170862 100644
--- a/customreply/customreply/Makefile
+++ b/customreply/customreply/Makefile
@@ -1,9 +1,8 @@
include theos/makefiles/common.mk
-export THEOS_DEVICE_IP=192.168.1.17
export ARCHS = armv7 arm64
export TARGET = iphone:clang:latest:8.0
-export SDKVERSION=8.2
+export SDKVERSION=8.1
BUNDLE_NAME = customreply
customreply_BUNDLE_EXTENSION = assistantPlugin
diff --git a/customreply/customreply/_/DEBIAN/control b/customreply/customreply/_/DEBIAN/control
new file mode 100644
index 0000000..c757ff9
--- /dev/null
+++ b/customreply/customreply/_/DEBIAN/control
@@ -0,0 +1,10 @@
+Package: com.assistantplus.customreply
+Name: customreply
+Depends: mobilesubstrate
+Architecture: iphoneos-arm
+Description: An awesome MobileSubstrate tweak!
+Maintainer: Zaid Elkurdi
+Author: Zaid Elkurdi
+Section: Tweaks
+Version: 0.0.1-23
+Installed-Size: 136
diff --git a/customreply/customreply/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/Info.plist b/customreply/customreply/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/Info.plist
new file mode 100644
index 0000000..d3aa735
--- /dev/null
+++ b/customreply/customreply/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/Info.plist
@@ -0,0 +1,37 @@
+
+
+
+
+ CFBundleName
+ CustomReply
+ CFBundleIdentifier
+ com.assistantplus.customreplyidentifier
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleVersion
+ 1
+ CFBundleDisplayName
+ CustomReply
+ MinimumOSVersion
+ 5.0
+ CFBundleSupportedPlatforms
+
+ iPhoneOS
+
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ AppBundleID
+ com.assistantplus.customreplyidentifier
+ UIDeviceFamily
+
+ 1
+ 2
+
+ CFBundleShortVersionString
+ 1.0
+ NSPrincipalClass
+ customreply
+
+
diff --git a/customreply/customreply/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/customreply b/customreply/customreply/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/customreply
new file mode 100755
index 0000000..1fab22c
Binary files /dev/null and b/customreply/customreply/_/Library/AssistantPlusPlugins/customreply.assistantPlugin/customreply differ
diff --git a/customreply/customreply/customReplyCommands.m b/customreply/customreply/customReplyCommands.m
index e0fb888..f00b635 100644
--- a/customreply/customreply/customReplyCommands.m
+++ b/customreply/customreply/customReplyCommands.m
@@ -14,11 +14,8 @@
}
- (void)createPhraseDictionary:(NSDictionary*)repliesDict {
- NSLog(@"AP Custom Replies: Creating phrase dictionary!");
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
- NSLog(@"AP Custom Replies: 2 %@", repliesDict);
NSArray *customReplies = repliesDict[@"customReplies"];
- NSLog(@"AP Custom Replies: Found %@", customReplies);
if (customReplies) {
for (NSDictionary *currReply in customReplies) {
NSString *currTrigger = currReply[@"trigger"];
@@ -33,7 +30,6 @@
}
}
phrases = dict;
- NSLog(@"new phrases is %@", phrases);
}
-(BOOL)handleSpeech:(NSString *)text withTokens:(NSSet *)tokens withSession:(id)session {
@@ -43,8 +39,6 @@
}
text = [[text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] lowercaseString];
- NSLog(@"Phrases: %@", phrases);
- NSLog(@"Searching for %@", text);
if ([phrases objectForKey:text]) {
NSArray *customReplies = [phrases objectForKey:text];
diff --git a/customreply/customreply/customreply.h b/customreply/customreply/customreply.h
index e496ef2..b1bb4a7 100644
--- a/customreply/customreply/customreply.h
+++ b/customreply/customreply/customreply.h
@@ -10,5 +10,5 @@
#import "AssistantPlusHeaders.h"
@interface customreply : NSObject
--(id)initWithSystem:(id)system;
+-(id)initWithPluginManager:(id)manager;
@end
diff --git a/customreply/customreply/customreply.m b/customreply/customreply/customreply.m
index 3768a46..b597311 100644
--- a/customreply/customreply/customreply.m
+++ b/customreply/customreply/customreply.m
@@ -11,10 +11,10 @@
@implementation customreply
--(id)initWithSystem:(id)system {
+-(id)initWithPluginManager:(id)manager {
if (self = [super init]) {
- if (system) {
- [system registerCommand:[customReplyCommands class]];
+ if (manager) {
+ [manager registerCommand:[customReplyCommands class]];
}
}
return self;
diff --git a/customreply/customreply/obj/customreply.assistantPlugin/customreply b/customreply/customreply/obj/customreply.assistantPlugin/customreply
index ebdb762..1fab22c 100755
Binary files a/customreply/customreply/obj/customreply.assistantPlugin/customreply and b/customreply/customreply/obj/customreply.assistantPlugin/customreply differ
diff --git a/spotifyPlugin/spotifyPlugin.xcodeproj/project.pbxproj b/spotifyPlugin/spotifyPlugin.xcodeproj/project.pbxproj
index 30523ab..3f22ce5 100644
--- a/spotifyPlugin/spotifyPlugin.xcodeproj/project.pbxproj
+++ b/spotifyPlugin/spotifyPlugin.xcodeproj/project.pbxproj
@@ -7,11 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
+ B86E66CA1ACA10BC007C6014 /* AFHTTPRequestSerializer+OAuth2.m in Sources */ = {isa = PBXBuildFile; fileRef = B86E66C71ACA10BC007C6014 /* AFHTTPRequestSerializer+OAuth2.m */; };
+ B86E66CB1ACA10BC007C6014 /* AFOAuth2Manager.m in Sources */ = {isa = PBXBuildFile; fileRef = B86E66C91ACA10BC007C6014 /* AFOAuth2Manager.m */; };
+ B86E66CE1ACA1E6F007C6014 /* SpotifyPlayCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = B86E66CD1ACA1E6F007C6014 /* SpotifyPlayCommands.m */; };
B8EC46F21AC7670700ED3836 /* libspotifyPlugin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8EC46E61AC7670700ED3836 /* libspotifyPlugin.a */; };
B8EC47031AC768A100ED3836 /* spotifySiri.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC47021AC768A100ED3836 /* spotifySiri.m */; };
- B8EC47061AC7690E00ED3836 /* spotifyCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC47051AC7690E00ED3836 /* spotifyCommands.m */; };
+ B8EC47061AC7690E00ED3836 /* SpotifySearchCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC47051AC7690E00ED3836 /* SpotifySearchCommands.m */; };
B8EC470A1AC76D1E00ED3836 /* SpotifySongTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC47091AC76D1E00ED3836 /* SpotifySongTableViewCell.m */; };
- B8EC470D1AC76E1000ED3836 /* spotifySongListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC470C1AC76E1000ED3836 /* spotifySongListViewController.m */; };
+ B8EC470D1AC76E1000ED3836 /* SpotifySearchResultsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC470C1AC76E1000ED3836 /* SpotifySearchResultsViewController.m */; };
B8EC47221AC7746E00ED3836 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC47101AC7746E00ED3836 /* AFHTTPRequestOperation.m */; };
B8EC47231AC7746E00ED3836 /* AFHTTPRequestOperationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC47121AC7746E00ED3836 /* AFHTTPRequestOperationManager.m */; };
B8EC47241AC7746E00ED3836 /* AFHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B8EC47141AC7746E00ED3836 /* AFHTTPSessionManager.m */; };
@@ -36,11 +39,11 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
- B8EC46E41AC7670700ED3836 /* CopyFiles */ = {
+ B86E66D71ACB560F007C6014 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
- dstPath = "include/$(PRODUCT_NAME)";
- dstSubfolderSpec = 16;
+ dstPath = "";
+ dstSubfolderSpec = 7;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -48,19 +51,25 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ B86E66C61ACA10BC007C6014 /* AFHTTPRequestSerializer+OAuth2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AFHTTPRequestSerializer+OAuth2.h"; sourceTree = ""; };
+ B86E66C71ACA10BC007C6014 /* AFHTTPRequestSerializer+OAuth2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AFHTTPRequestSerializer+OAuth2.m"; sourceTree = ""; };
+ B86E66C81ACA10BC007C6014 /* AFOAuth2Manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFOAuth2Manager.h; sourceTree = ""; };
+ B86E66C91ACA10BC007C6014 /* AFOAuth2Manager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFOAuth2Manager.m; sourceTree = ""; };
+ B86E66CC1ACA1E6F007C6014 /* SpotifyPlayCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpotifyPlayCommands.h; sourceTree = ""; };
+ B86E66CD1ACA1E6F007C6014 /* SpotifyPlayCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpotifyPlayCommands.m; sourceTree = ""; };
B8EC46E61AC7670700ED3836 /* libspotifyPlugin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libspotifyPlugin.a; sourceTree = BUILT_PRODUCTS_DIR; };
B8EC46F11AC7670700ED3836 /* spotifyPluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = spotifyPluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B8EC46F71AC7670700ED3836 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
B8EC47001AC7689100ED3836 /* AssistantPlusHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AssistantPlusHeaders.h; sourceTree = ""; };
B8EC47011AC768A100ED3836 /* spotifySiri.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spotifySiri.h; sourceTree = ""; };
B8EC47021AC768A100ED3836 /* spotifySiri.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = spotifySiri.m; sourceTree = ""; };
- B8EC47041AC7690E00ED3836 /* spotifyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spotifyCommands.h; sourceTree = ""; };
- B8EC47051AC7690E00ED3836 /* spotifyCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = spotifyCommands.m; sourceTree = ""; };
+ B8EC47041AC7690E00ED3836 /* SpotifySearchCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpotifySearchCommands.h; sourceTree = ""; };
+ B8EC47051AC7690E00ED3836 /* SpotifySearchCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpotifySearchCommands.m; sourceTree = ""; };
B8EC47071AC76B8800ED3836 /* SpotifySongTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SpotifySongTableViewCell.xib; sourceTree = ""; };
B8EC47081AC76D1E00ED3836 /* SpotifySongTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpotifySongTableViewCell.h; sourceTree = ""; };
B8EC47091AC76D1E00ED3836 /* SpotifySongTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpotifySongTableViewCell.m; sourceTree = ""; };
- B8EC470B1AC76E1000ED3836 /* spotifySongListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spotifySongListViewController.h; sourceTree = ""; };
- B8EC470C1AC76E1000ED3836 /* spotifySongListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = spotifySongListViewController.m; sourceTree = ""; };
+ B8EC470B1AC76E1000ED3836 /* SpotifySearchResultsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpotifySearchResultsViewController.h; sourceTree = ""; };
+ B8EC470C1AC76E1000ED3836 /* SpotifySearchResultsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpotifySearchResultsViewController.m; sourceTree = ""; };
B8EC470F1AC7746E00ED3836 /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperation.h; sourceTree = ""; };
B8EC47101AC7746E00ED3836 /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPRequestOperation.m; sourceTree = ""; };
B8EC47111AC7746E00ED3836 /* AFHTTPRequestOperationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperationManager.h; sourceTree = ""; };
@@ -129,10 +138,12 @@
B8EC47001AC7689100ED3836 /* AssistantPlusHeaders.h */,
B8EC47011AC768A100ED3836 /* spotifySiri.h */,
B8EC47021AC768A100ED3836 /* spotifySiri.m */,
- B8EC47041AC7690E00ED3836 /* spotifyCommands.h */,
- B8EC47051AC7690E00ED3836 /* spotifyCommands.m */,
- B8EC470B1AC76E1000ED3836 /* spotifySongListViewController.h */,
- B8EC470C1AC76E1000ED3836 /* spotifySongListViewController.m */,
+ B86E66CC1ACA1E6F007C6014 /* SpotifyPlayCommands.h */,
+ B86E66CD1ACA1E6F007C6014 /* SpotifyPlayCommands.m */,
+ B8EC47041AC7690E00ED3836 /* SpotifySearchCommands.h */,
+ B8EC47051AC7690E00ED3836 /* SpotifySearchCommands.m */,
+ B8EC470B1AC76E1000ED3836 /* SpotifySearchResultsViewController.h */,
+ B8EC470C1AC76E1000ED3836 /* SpotifySearchResultsViewController.m */,
B8EC47081AC76D1E00ED3836 /* SpotifySongTableViewCell.h */,
B8EC47091AC76D1E00ED3836 /* SpotifySongTableViewCell.m */,
B8EC47071AC76B8800ED3836 /* SpotifySongTableViewCell.xib */,
@@ -160,6 +171,10 @@
B8EC470E1AC7746E00ED3836 /* AFNetworking */ = {
isa = PBXGroup;
children = (
+ B86E66C61ACA10BC007C6014 /* AFHTTPRequestSerializer+OAuth2.h */,
+ B86E66C71ACA10BC007C6014 /* AFHTTPRequestSerializer+OAuth2.m */,
+ B86E66C81ACA10BC007C6014 /* AFOAuth2Manager.h */,
+ B86E66C91ACA10BC007C6014 /* AFOAuth2Manager.m */,
B8EC470F1AC7746E00ED3836 /* AFHTTPRequestOperation.h */,
B8EC47101AC7746E00ED3836 /* AFHTTPRequestOperation.m */,
B8EC47111AC7746E00ED3836 /* AFHTTPRequestOperationManager.h */,
@@ -192,7 +207,7 @@
buildPhases = (
B8EC46E21AC7670700ED3836 /* Sources */,
B8EC46E31AC7670700ED3836 /* Frameworks */,
- B8EC46E41AC7670700ED3836 /* CopyFiles */,
+ B86E66D71ACB560F007C6014 /* CopyFiles */,
);
buildRules = (
);
@@ -272,19 +287,22 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- B8EC47061AC7690E00ED3836 /* spotifyCommands.m in Sources */,
+ B8EC47061AC7690E00ED3836 /* SpotifySearchCommands.m in Sources */,
+ B86E66CB1ACA10BC007C6014 /* AFOAuth2Manager.m in Sources */,
B8EC47261AC7746E00ED3836 /* AFSecurityPolicy.m in Sources */,
B8EC47031AC768A100ED3836 /* spotifySiri.m in Sources */,
B8EC47231AC7746E00ED3836 /* AFHTTPRequestOperationManager.m in Sources */,
B8EC47271AC7746E00ED3836 /* AFURLConnectionOperation.m in Sources */,
B8EC47221AC7746E00ED3836 /* AFHTTPRequestOperation.m in Sources */,
- B8EC470D1AC76E1000ED3836 /* spotifySongListViewController.m in Sources */,
+ B8EC470D1AC76E1000ED3836 /* SpotifySearchResultsViewController.m in Sources */,
B8EC47281AC7746E00ED3836 /* AFURLRequestSerialization.m in Sources */,
B8EC47241AC7746E00ED3836 /* AFHTTPSessionManager.m in Sources */,
B8EC47251AC7746E00ED3836 /* AFNetworkReachabilityManager.m in Sources */,
B8EC47291AC7746E00ED3836 /* AFURLResponseSerialization.m in Sources */,
+ B86E66CA1ACA10BC007C6014 /* AFHTTPRequestSerializer+OAuth2.m in Sources */,
B8EC472A1AC7746E00ED3836 /* AFURLSessionManager.m in Sources */,
B8EC472C1AC779D700ED3836 /* Makefile in Sources */,
+ B86E66CE1ACA1E6F007C6014 /* SpotifyPlayCommands.m in Sources */,
B8EC470A1AC76D1E00ED3836 /* SpotifySongTableViewCell.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -384,6 +402,7 @@
B8EC46FB1AC7670700ED3836 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CODE_SIGN_IDENTITY = "iPhone Developer: Zaid Elkurdi (4W5YEE5275)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -393,6 +412,7 @@
B8EC46FC1AC7670700ED3836 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CODE_SIGN_IDENTITY = "iPhone Developer: Zaid Elkurdi (4W5YEE5275)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -402,6 +422,7 @@
B8EC46FE1AC7670700ED3836 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CODE_SIGN_IDENTITY = "iPhone Developer: Zaid Elkurdi (4W5YEE5275)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
@@ -419,6 +440,7 @@
B8EC46FF1AC7670700ED3836 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CODE_SIGN_IDENTITY = "iPhone Developer: Zaid Elkurdi (4W5YEE5275)";
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
@@ -448,6 +470,7 @@
B8EC46FC1AC7670700ED3836 /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
B8EC46FD1AC7670700ED3836 /* Build configuration list for PBXNativeTarget "spotifyPluginTests" */ = {
isa = XCConfigurationList;
@@ -456,6 +479,7 @@
B8EC46FF1AC7670700ED3836 /* Release */,
);
defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
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 05427ab..aa9ebd3 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.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/spotifyPlugin/spotifyPlugin.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..fe2b454
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin.xcodeproj/xcuserdata/Zaid.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,5 @@
+
+
+
diff --git a/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-0.0.1 b/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-0.0.1
new file mode 100644
index 0000000..9d1ce53
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-0.0.1
@@ -0,0 +1 @@
+82
\ No newline at end of file
diff --git a/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-1.0.0 b/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-1.0.0
new file mode 100644
index 0000000..62f9457
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/.theos/packages/com.assistantplus.spotifycontrols-1.0.0
@@ -0,0 +1 @@
+6
\ No newline at end of file
diff --git a/spotifyPlugin/spotifyPlugin/AFNetworking/AFHTTPRequestSerializer+OAuth2.h b/spotifyPlugin/spotifyPlugin/AFNetworking/AFHTTPRequestSerializer+OAuth2.h
new file mode 100755
index 0000000..4ecd228
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/AFNetworking/AFHTTPRequestSerializer+OAuth2.h
@@ -0,0 +1,36 @@
+// AFHTTPRequestSerializer+OAuth2.h
+//
+// Copyright (c) 2012-2014 AFNetworking (http://afnetworking.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFURLRequestSerialization.h"
+
+@class AFOAuthCredential;
+
+@interface AFHTTPRequestSerializer (OAuth2)
+
+/**
+ Sets the "Authorization" HTTP header set in request objects made by the HTTP client to contain the access token within the OAuth credential. This overwrites any existing value for this header.
+
+ @param credential The OAuth2 credential
+ */
+- (void)setAuthorizationHeaderFieldWithCredential:(AFOAuthCredential *)credential;
+
+@end
diff --git a/spotifyPlugin/spotifyPlugin/AFNetworking/AFHTTPRequestSerializer+OAuth2.m b/spotifyPlugin/spotifyPlugin/AFNetworking/AFHTTPRequestSerializer+OAuth2.m
new file mode 100755
index 0000000..fac08b1
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/AFNetworking/AFHTTPRequestSerializer+OAuth2.m
@@ -0,0 +1,34 @@
+// AFHTTPRequestSerializer+OAuth2.m
+//
+// Copyright (c) 2012-2014 AFNetworking (http://afnetworking.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFHTTPRequestSerializer+OAuth2.h"
+#import "AFOAuth2Manager.h"
+
+@implementation AFHTTPRequestSerializer (OAuth2)
+
+- (void)setAuthorizationHeaderFieldWithCredential:(AFOAuthCredential *)credential {
+ if ([credential.tokenType compare:@"Bearer" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
+ [self setValue:[NSString stringWithFormat:@"Bearer %@", credential.accessToken] forHTTPHeaderField:@"Authorization"];
+ }
+}
+
+@end
diff --git a/spotifyPlugin/spotifyPlugin/AFNetworking/AFOAuth2Manager.h b/spotifyPlugin/spotifyPlugin/AFNetworking/AFOAuth2Manager.h
new file mode 100755
index 0000000..ceb5b5b
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/AFNetworking/AFOAuth2Manager.h
@@ -0,0 +1,325 @@
+// AFOAuth2Manager.h
+//
+// Copyright (c) 2012-2014 AFNetworking (http://afnetworking.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import
+
+#import "AFHTTPRequestOperationManager.h"
+
+@class AFOAuthCredential;
+
+/**
+ `AFOAuth2Manager` encapsulates common patterns to authenticate against a resource server conforming to the behavior outlined in the OAuth 2.0 specification.
+
+ In your application, it is recommended that you use `AFOAuth2Manager` exclusively to get an authorization token, which is then passed to another `AFHTTPClient` subclass.
+
+ @see RFC 6749 The OAuth 2.0 Authorization Framework: http://tools.ietf.org/html/rfc6749
+ */
+@interface AFOAuth2Manager : AFHTTPRequestOperationManager
+
+///------------------------------------------
+/// @name Accessing OAuth 2 Client Properties
+///------------------------------------------
+
+/**
+ The service provider identifier used to store and retrieve OAuth credentials by `AFOAuthCredential`. Equivalent to the hostname of the client `baseURL`.
+ */
+@property (readonly, nonatomic, copy) NSString *serviceProviderIdentifier;
+
+/**
+ The client identifier issued by the authorization server, uniquely representing the registration information provided by the client.
+ */
+@property (readonly, nonatomic, copy) NSString *clientID;
+
+/**
+ Whether to encode client credentials in a Base64-encoded HTTP `Authorization` header, as opposed to the request body. Defaults to `YES`.
+ */
+@property (nonatomic, assign) BOOL useHTTPBasicAuthentication;
+
+///------------------------------------------------
+/// @name Creating and Initializing OAuth 2 Clients
+///------------------------------------------------
+
+/**
+ Creates and initializes an `AFOAuth2Manager` object with the specified base URL, client identifier, and secret.
+
+ @param url The base URL for the HTTP client. This argument must not be `nil`.
+ @param clientID The client identifier issued by the authorization server, uniquely representing the registration information provided by the client. This argument must not be `nil`.
+ @param secret The client secret.
+
+ @return The newly-initialized OAuth 2 client
+ */
++ (instancetype)clientWithBaseURL:(NSURL *)url
+ clientID:(NSString *)clientID
+ secret:(NSString *)secret;
+
+/**
+ Initializes an `AFOAuth2Manager` object with the specified base URL, client identifier, and secret. The communication to to the server will use HTTP basic auth by default (use `-(id)initWithBaseURL:clientID:secret:withBasicAuth:` to change this).
+
+ @param url The base URL for the HTTP client. This argument must not be `nil`.
+ @param clientID The client identifier issued by the authorization server, uniquely representing the registration information provided by the client. This argument must not be `nil`.
+ @param secret The client secret.
+
+ @return The newly-initialized OAuth 2 client
+ */
+- (id)initWithBaseURL:(NSURL *)url
+ clientID:(NSString *)clientID
+ secret:(NSString *)secret;
+
+///---------------------
+/// @name Authenticating
+///---------------------
+
+/**
+ Creates and enqueues an `AFHTTPRequestOperation` to authenticate against the server using a specified username and password, with a designated scope.
+
+ @param URLString The URL string used to create the request URL.
+ @param username The username used for authentication
+ @param password The password used for authentication
+ @param scope The authorization scope
+ @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single argument: the OAuth credential returned by the server.
+ @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error returned from the server.
+ */
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ username:(NSString *)username
+ password:(NSString *)password
+ scope:(NSString *)scope
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure;
+
+/**
+ Creates and enqueues an `AFHTTPRequestOperation` to authenticate against the server with a designated scope.
+
+ @param URLString The URL string used to create the request URL.
+ @param scope The authorization scope
+ @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single argument: the OAuth credential returned by the server.
+ @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error returned from the server.
+ */
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ scope:(NSString *)scope
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure;
+
+/**
+ Creates and enqueues an `AFHTTPRequestOperation` to authenticate against the server using the specified refresh token.
+
+ @param URLString The URL string used to create the request URL.
+ @param refreshToken The OAuth refresh token
+ @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single argument: the OAuth credential returned by the server.
+ @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error returned from the server.
+ */
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ refreshToken:(NSString *)refreshToken
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure;
+
+/**
+ Creates and enqueues an `AFHTTPRequestOperation` to authenticate against the server with an authorization code, redirecting to a specified URI upon successful authentication.
+
+ @param URLString The URL string used to create the request URL.
+ @param code The authorization code
+ @param uri The URI to redirect to after successful authentication
+ @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single argument: the OAuth credential returned by the server.
+ @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error returned from the server.
+ */
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ code:(NSString *)code
+ redirectURI:(NSString *)uri
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure;
+
+/**
+ Creates and enqueues an `AFHTTPRequestOperation` to authenticate against the server with the specified parameters.
+
+ @param URLString The URL string used to create the request URL.
+ @param parameters The parameters to be encoded and set in the request HTTP body.
+ @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single argument: the OAuth credential returned by the server.
+ @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the error returned from the server.
+ */
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ parameters:(NSDictionary *)parameters
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure;
+
+@end
+
+#pragma mark -
+
+/**
+ `AFOAuthCredential` models the credentials returned from an OAuth server, storing the token type, access & refresh tokens, and whether the token is expired.
+
+ OAuth credentials can be stored in the user's keychain, and retrieved on subsequent launches.
+ */
+@interface AFOAuthCredential : NSObject
+
+///--------------------------------------
+/// @name Accessing Credential Properties
+///--------------------------------------
+
+/**
+ The OAuth access token.
+ */
+@property (readonly, nonatomic, copy) NSString *accessToken;
+
+/**
+ The OAuth token type (e.g. "bearer").
+ */
+@property (readonly, nonatomic, copy) NSString *tokenType;
+
+/**
+ The OAuth refresh token.
+ */
+@property (readonly, nonatomic, copy) NSString *refreshToken;
+
+/**
+ Whether the OAuth credentials are expired.
+ */
+@property (readonly, nonatomic, assign, getter = isExpired) BOOL expired;
+
+///--------------------------------------------
+/// @name Creating and Initializing Credentials
+///--------------------------------------------
+
+/**
+ Create an OAuth credential from a token string, with a specified type.
+
+ @param token The OAuth token string.
+ @param type The OAuth token type.
+ */
++ (instancetype)credentialWithOAuthToken:(NSString *)token
+ tokenType:(NSString *)type;
+
+/**
+ Initialize an OAuth credential from a token string, with a specified type.
+
+ @param token The OAuth token string.
+ @param type The OAuth token type.
+ */
+- (id)initWithOAuthToken:(NSString *)token
+ tokenType:(NSString *)type;
+
+///----------------------------
+/// @name Setting Refresh Token
+///----------------------------
+
+/**
+ Set the credential refresh token, without a specific expiration
+
+ @param refreshToken The OAuth refresh token.
+ */
+- (void)setRefreshToken:(NSString *)refreshToken;
+
+
+/**
+ Set the expiration on the access token. If no expiration is given by the OAuth2 provider,
+ you may pass in [NSDate distantFuture]
+
+ @param expiration The expiration of the access token. This must not be `nil`.
+ */
+- (void)setExpiration:(NSDate *)expiration;
+
+/**
+ Set the credential refresh token, with a specified expiration.
+
+ @param refreshToken The OAuth refresh token.
+ @param expiration The expiration of the access token. This must not be `nil`.
+ */
+- (void)setRefreshToken:(NSString *)refreshToken
+ expiration:(NSDate *)expiration;
+
+///-----------------------------------------
+/// @name Storing and Retrieving Credentials
+///-----------------------------------------
+
+/**
+ Stores the specified OAuth credential for a given web service identifier in the Keychain.
+ with the default Keychain Accessibilty of kSecAttrAccessibleWhenUnlocked.
+
+ @param credential The OAuth credential to be stored.
+ @param identifier The service identifier associated with the specified credential.
+
+ @return Whether or not the credential was stored in the keychain.
+ */
++ (BOOL)storeCredential:(AFOAuthCredential *)credential
+ withIdentifier:(NSString *)identifier;
+
+/**
+ Stores the specified OAuth token for a given web service identifier in the Keychain.
+
+ @param credential The OAuth credential to be stored.
+ @param identifier The service identifier associated with the specified token.
+ @param securityAccessibility The Keychain security accessibility to store the credential with.
+
+ @return Whether or not the credential was stored in the keychain.
+ */
++ (BOOL)storeCredential:(AFOAuthCredential *)credential
+ withIdentifier:(NSString *)identifier
+ withAccessibility:(id)securityAccessibility;
+
+/**
+ Retrieves the OAuth credential stored with the specified service identifier from the Keychain.
+
+ @param identifier The service identifier associated with the specified credential.
+
+ @return The retrieved OAuth credential.
+ */
++ (AFOAuthCredential *)retrieveCredentialWithIdentifier:(NSString *)identifier;
+
+/**
+ Deletes the OAuth credential stored with the specified service identifier from the Keychain.
+
+ @param identifier The service identifier associated with the specified credential.
+
+ @return Whether or not the credential was deleted from the keychain.
+ */
++ (BOOL)deleteCredentialWithIdentifier:(NSString *)identifier;
+
+@end
+
+///----------------
+/// @name Constants
+///----------------
+
+/**
+ ## Error Domains
+
+ The following error domain is predefined.
+
+ - `NSString * const AFOAuth2ErrorDomain`
+
+ ## OAuth Grant Types
+
+ OAuth 2.0 provides several grant types, covering several different use cases. The following grant type string constants are provided:
+
+ `kAFOAuthCodeGrantType`: "authorization_code"
+ `kAFOAuthClientCredentialsGrantType`: "client_credentials"
+ `kAFOAuthPasswordCredentialsGrantType`: "password"
+ `kAFOAuthRefreshGrantType`: "refresh_token"
+ */
+extern NSString * const AFOAuth2ErrorDomain;
+
+extern NSString * const kAFOAuthCodeGrantType;
+extern NSString * const kAFOAuthClientCredentialsGrantType;
+extern NSString * const kAFOAuthPasswordCredentialsGrantType;
+extern NSString * const kAFOAuthRefreshGrantType;
+
+@compatibility_alias AFOAuth2Client AFOAuth2Manager;
+@compatibility_alias AFOAuth2RequestOperationManager AFOAuth2Manager;
diff --git a/spotifyPlugin/spotifyPlugin/AFNetworking/AFOAuth2Manager.m b/spotifyPlugin/spotifyPlugin/AFNetworking/AFOAuth2Manager.m
new file mode 100755
index 0000000..0f579e0
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/AFNetworking/AFOAuth2Manager.m
@@ -0,0 +1,436 @@
+// AFOAuth2Manager.m
+//
+// Copyright (c) 2012-2014 AFNetworking (http://afnetworking.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import
+
+#import "AFOAuth2Manager.h"
+
+NSString * const AFOAuth2ErrorDomain = @"com.alamofire.networking.oauth2.error";
+
+NSString * const kAFOAuthCodeGrantType = @"authorization_code";
+NSString * const kAFOAuthClientCredentialsGrantType = @"client_credentials";
+NSString * const kAFOAuthPasswordCredentialsGrantType = @"password";
+NSString * const kAFOAuthRefreshGrantType = @"refresh_token";
+
+NSString * const kAFOAuth2CredentialServiceName = @"AFOAuthCredentialService";
+
+static NSDictionary * AFKeychainQueryDictionaryWithIdentifier(NSString *identifier) {
+ NSCParameterAssert(identifier);
+
+ return @{
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
+ (__bridge id)kSecAttrService: kAFOAuth2CredentialServiceName,
+ (__bridge id)kSecAttrAccount: identifier
+ };
+}
+
+// See: http://tools.ietf.org/html/rfc6749#section-5.2
+static NSError * AFErrorFromRFC6749Section5_2Error(id object) {
+ if (![object valueForKey:@"error"] || [[object valueForKey:@"error"] isEqual:[NSNull null]]) {
+ return nil;
+ }
+
+ NSMutableDictionary *mutableUserInfo = [NSMutableDictionary dictionary];
+
+ NSString *description = nil;
+ if ([object valueForKey:@"error_description"]) {
+ description = [object valueForKey:@"error_description"];
+ } else {
+ if ([[object valueForKey:@"error"] isEqualToString:@"invalid_request"]) {
+ description = NSLocalizedStringFromTable(@"The request is missing a required parameter, includes an unsupported parameter value (other than grant type), repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed.", @"AFOAuth2Manager", @"invalid_request");
+ } else if ([[object valueForKey:@"error"] isEqualToString:@"invalid_client"]) {
+ description = NSLocalizedStringFromTable(@"Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the \"Authorization\" request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the \"WWW-Authenticate\" response header field matching the authentication scheme used by the client.", @"AFOAuth2Manager", @"invalid_request");
+ } else if ([[object valueForKey:@"error"] isEqualToString:@"invalid_grant"]) {
+ description = NSLocalizedStringFromTable(@"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.", @"AFOAuth2Manager", @"invalid_request");
+ } else if ([[object valueForKey:@"error"] isEqualToString:@"unauthorized_client"]) {
+ description = NSLocalizedStringFromTable(@"The authenticated client is not authorized to use this authorization grant type.", @"AFOAuth2Manager", @"invalid_request");
+ } else if ([[object valueForKey:@"error"] isEqualToString:@"unsupported_grant_type"]) {
+ description = NSLocalizedStringFromTable(@"The authorization grant type is not supported by the authorization server.", @"AFOAuth2Manager", @"invalid_request");
+ }
+ }
+
+ if (description) {
+ mutableUserInfo[NSLocalizedDescriptionKey] = description;
+ }
+
+ if ([object valueForKey:@"error_uri"]) {
+ mutableUserInfo[NSLocalizedRecoverySuggestionErrorKey] = [object valueForKey:@"error_uri"];
+ }
+
+ return [NSError errorWithDomain:AFOAuth2ErrorDomain code:-1 userInfo:mutableUserInfo];
+}
+
+#pragma mark -
+
+@interface AFOAuth2Manager ()
+@property (readwrite, nonatomic, copy) NSString *serviceProviderIdentifier;
+@property (readwrite, nonatomic, copy) NSString *clientID;
+@property (readwrite, nonatomic, copy) NSString *secret;
+@end
+
+@implementation AFOAuth2Manager
+
++ (instancetype)clientWithBaseURL:(NSURL *)url
+ clientID:(NSString *)clientID
+ secret:(NSString *)secret
+{
+ return [[self alloc] initWithBaseURL:url clientID:clientID secret:secret];
+}
+
+- (id)initWithBaseURL:(NSURL *)url
+ clientID:(NSString *)clientID
+ secret:(NSString *)secret
+{
+ NSParameterAssert(clientID);
+
+ self = [super initWithBaseURL:url];
+ if (!self) {
+ return nil;
+ }
+
+ self.serviceProviderIdentifier = [self.baseURL host];
+ self.clientID = clientID;
+ self.secret = secret;
+
+ self.useHTTPBasicAuthentication = YES;
+
+ [self.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
+
+ return self;
+}
+
+#pragma mark -
+
+- (void)setUseHTTPBasicAuthentication:(BOOL)useHTTPBasicAuthentication {
+ _useHTTPBasicAuthentication = useHTTPBasicAuthentication;
+
+ if (self.useHTTPBasicAuthentication) {
+ [self.requestSerializer setAuthorizationHeaderFieldWithUsername:self.clientID password:self.secret];
+ } else {
+ [self.requestSerializer setValue:nil forHTTPHeaderField:@"Authorization"];
+ }
+}
+
+- (void)setSecret:(NSString *)secret {
+ if (!secret) {
+ secret = @"";
+ }
+
+ _secret = secret;
+}
+
+#pragma mark -
+
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ username:(NSString *)username
+ password:(NSString *)password
+ scope:(NSString *)scope
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure
+{
+ NSParameterAssert(username);
+ NSParameterAssert(password);
+ NSParameterAssert(scope);
+
+ NSDictionary *parameters = @{
+ @"grant_type": kAFOAuthPasswordCredentialsGrantType,
+ @"username": username,
+ @"password": password,
+ @"scope": scope
+ };
+
+ return [self authenticateUsingOAuthWithURLString:URLString parameters:parameters success:success failure:failure];
+}
+
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ scope:(NSString *)scope
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure
+{
+ NSParameterAssert(scope);
+
+ NSDictionary *parameters = @{
+ @"grant_type": kAFOAuthClientCredentialsGrantType,
+ @"scope": scope
+ };
+
+ return [self authenticateUsingOAuthWithURLString:URLString parameters:parameters success:success failure:failure];
+}
+
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ refreshToken:(NSString *)refreshToken
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure
+{
+ NSParameterAssert(refreshToken);
+
+ NSDictionary *parameters = @{
+ @"grant_type": kAFOAuthRefreshGrantType,
+ @"refresh_token": refreshToken
+ };
+
+ return [self authenticateUsingOAuthWithURLString:URLString parameters:parameters success:success failure:failure];
+}
+
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ code:(NSString *)code
+ redirectURI:(NSString *)uri
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure
+{
+ NSParameterAssert(code);
+ NSParameterAssert(uri);
+
+ NSDictionary *parameters = @{
+ @"grant_type": kAFOAuthCodeGrantType,
+ @"code": code,
+ @"redirect_uri": uri
+ };
+
+ return [self authenticateUsingOAuthWithURLString:URLString parameters:parameters success:success failure:failure];
+}
+
+- (AFHTTPRequestOperation *)authenticateUsingOAuthWithURLString:(NSString *)URLString
+ parameters:(NSDictionary *)parameters
+ success:(void (^)(AFOAuthCredential *credential))success
+ failure:(void (^)(NSError *error))failure
+{
+ NSMutableDictionary *mutableParameters = [NSMutableDictionary dictionaryWithDictionary:parameters];
+ if (!self.useHTTPBasicAuthentication) {
+ mutableParameters[@"client_id"] = self.clientID;
+ mutableParameters[@"client_secret"] = self.secret;
+ }
+ parameters = [NSDictionary dictionaryWithDictionary:mutableParameters];
+
+ AFHTTPRequestOperation *requestOperation = [self POST:URLString parameters:parameters success:^(__unused AFHTTPRequestOperation *operation, id responseObject) {
+ if (!responseObject) {
+ if (failure) {
+ failure(nil);
+ }
+
+ return;
+ }
+
+ if ([responseObject valueForKey:@"error"]) {
+ if (failure) {
+ failure(AFErrorFromRFC6749Section5_2Error(responseObject));
+ }
+
+ return;
+ }
+
+ NSString *refreshToken = [responseObject valueForKey:@"refresh_token"];
+ if (!refreshToken || [refreshToken isEqual:[NSNull null]]) {
+ refreshToken = [parameters valueForKey:@"refresh_token"];
+ }
+
+ AFOAuthCredential *credential = [AFOAuthCredential credentialWithOAuthToken:[responseObject valueForKey:@"access_token"] tokenType:[responseObject valueForKey:@"token_type"]];
+
+
+ if (refreshToken) { // refreshToken is optional in the OAuth2 spec
+ [credential setRefreshToken:refreshToken];
+ }
+
+ // Expiration is optional, but recommended in the OAuth2 spec. It not provide, assume distantFuture === never expires
+ NSDate *expireDate = [NSDate distantFuture];
+ id expiresIn = [responseObject valueForKey:@"expires_in"];
+ if (expiresIn && ![expiresIn isEqual:[NSNull null]]) {
+ expireDate = [NSDate dateWithTimeIntervalSinceNow:[expiresIn doubleValue]];
+ }
+
+ if (expireDate) {
+ [credential setExpiration:expireDate];
+ }
+
+ if (success) {
+ success(credential);
+ }
+ } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
+ if (failure) {
+ failure(error);
+ }
+ }];
+
+ return requestOperation;
+}
+
+@end
+
+#pragma mark -
+
+@interface AFOAuthCredential ()
+@property (readwrite, nonatomic, copy) NSString *accessToken;
+@property (readwrite, nonatomic, copy) NSString *tokenType;
+@property (readwrite, nonatomic, copy) NSString *refreshToken;
+@property (readwrite, nonatomic, copy) NSDate *expiration;
+@end
+
+@implementation AFOAuthCredential
+@dynamic expired;
+
+#pragma mark -
+
++ (instancetype)credentialWithOAuthToken:(NSString *)token
+ tokenType:(NSString *)type
+{
+ return [[self alloc] initWithOAuthToken:token tokenType:type];
+}
+
+- (id)initWithOAuthToken:(NSString *)token
+ tokenType:(NSString *)type
+{
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ self.accessToken = token;
+ self.tokenType = type;
+
+ return self;
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@ accessToken:\"%@\" tokenType:\"%@\" refreshToken:\"%@\" expiration:\"%@\">", [self class], self.accessToken, self.tokenType, self.refreshToken, self.expiration];
+}
+
+- (void)setRefreshToken:(NSString *)refreshToken
+{
+ _refreshToken = refreshToken;
+}
+
+- (void)setExpiration:(NSDate *)expiration
+{
+ _expiration = expiration;
+}
+
+- (void)setRefreshToken:(NSString *)refreshToken
+ expiration:(NSDate *)expiration
+{
+ NSParameterAssert(refreshToken);
+ NSParameterAssert(expiration);
+
+ self.refreshToken = refreshToken;
+ self.expiration = expiration;
+}
+
+- (BOOL)isExpired {
+ return [self.expiration compare:[NSDate date]] == NSOrderedAscending;
+}
+
+#pragma mark Keychain
+
++ (BOOL)storeCredential:(AFOAuthCredential *)credential
+ withIdentifier:(NSString *)identifier
+{
+ id securityAccessibility = nil;
+#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 43000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
+ if (&kSecAttrAccessibleWhenUnlocked != NULL) {
+ securityAccessibility = (__bridge id)kSecAttrAccessibleWhenUnlocked;
+ }
+#endif
+
+ return [[self class] storeCredential:credential withIdentifier:identifier withAccessibility:securityAccessibility];
+}
+
++ (BOOL)storeCredential:(AFOAuthCredential *)credential
+ withIdentifier:(NSString *)identifier
+ withAccessibility:(id)securityAccessibility
+{
+ NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier) mutableCopy];
+
+ if (!credential) {
+ return [self deleteCredentialWithIdentifier:identifier];
+ }
+
+ NSMutableDictionary *updateDictionary = [NSMutableDictionary dictionary];
+ updateDictionary[(__bridge id)kSecValueData] = [NSKeyedArchiver archivedDataWithRootObject:credential];
+
+ if (securityAccessibility) {
+ updateDictionary[(__bridge id)kSecAttrAccessible] = securityAccessibility;
+ }
+
+ OSStatus status;
+ BOOL exists = ([self retrieveCredentialWithIdentifier:identifier] != nil);
+
+ if (exists) {
+ status = SecItemUpdate((__bridge CFDictionaryRef)queryDictionary, (__bridge CFDictionaryRef)updateDictionary);
+ } else {
+ [queryDictionary addEntriesFromDictionary:updateDictionary];
+ status = SecItemAdd((__bridge CFDictionaryRef)queryDictionary, NULL);
+ }
+
+ if (status != errSecSuccess) {
+ NSLog(@"Unable to %@ credential with identifier \"%@\" (Error %li)", exists ? @"update" : @"add", identifier, (long int)status);
+ }
+
+ return (status == errSecSuccess);
+}
+
++ (BOOL)deleteCredentialWithIdentifier:(NSString *)identifier {
+ NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier) mutableCopy];
+
+ OSStatus status = SecItemDelete((__bridge CFDictionaryRef)queryDictionary);
+
+ if (status != errSecSuccess) {
+ NSLog(@"Unable to delete credential with identifier \"%@\" (Error %li)", identifier, (long int)status);
+ }
+
+ return (status == errSecSuccess);
+}
+
++ (AFOAuthCredential *)retrieveCredentialWithIdentifier:(NSString *)identifier {
+ NSMutableDictionary *queryDictionary = [AFKeychainQueryDictionaryWithIdentifier(identifier) mutableCopy];
+ queryDictionary[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue;
+ queryDictionary[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
+
+ CFDataRef result = nil;
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)queryDictionary, (CFTypeRef *)&result);
+
+ if (status != errSecSuccess) {
+ NSLog(@"Unable to fetch credential with identifier \"%@\" (Error %li)", identifier, (long int)status);
+ return nil;
+ }
+
+ return [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)result];
+}
+
+#pragma mark - NSCoding
+
+- (id)initWithCoder:(NSCoder *)decoder {
+ self = [super init];
+ self.accessToken = [decoder decodeObjectForKey:NSStringFromSelector(@selector(accessToken))];
+ self.tokenType = [decoder decodeObjectForKey:NSStringFromSelector(@selector(tokenType))];
+ self.refreshToken = [decoder decodeObjectForKey:NSStringFromSelector(@selector(refreshToken))];
+ self.expiration = [decoder decodeObjectForKey:NSStringFromSelector(@selector(expiration))];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)encoder {
+ [encoder encodeObject:self.accessToken forKey:NSStringFromSelector(@selector(accessToken))];
+ [encoder encodeObject:self.tokenType forKey:NSStringFromSelector(@selector(tokenType))];
+ [encoder encodeObject:self.refreshToken forKey:NSStringFromSelector(@selector(refreshToken))];
+ [encoder encodeObject:self.expiration forKey:NSStringFromSelector(@selector(expiration))];
+}
+
+@end
diff --git a/spotifyPlugin/spotifyPlugin/AssistantPlusHeaders.h b/spotifyPlugin/spotifyPlugin/AssistantPlusHeaders.h
index 2ac672e..0e73d5c 100644
--- a/spotifyPlugin/spotifyPlugin/AssistantPlusHeaders.h
+++ b/spotifyPlugin/spotifyPlugin/AssistantPlusHeaders.h
@@ -1,64 +1,99 @@
//
// AssistantPlusHeaders.h
-//
+// For use with Assistant+ v1.0
//
-// Created by Zaid Elkurdi on 3/12/15.
+// Created by Zaid Elkurdi on 2/28/15.
//
//
#ifndef _AssistantPlusHeaders_h
#define _AssistantPlusHeaders_h
+// Represents the current Siri session
@protocol APSiriSession
+/* Send a simple text snippet that Siri will read.
+
+ temporary: If true then the snippet will be replaced by the next view sent to the session.
+ For this to function properly the dialogPhase should be "Reflection"
+ scrollToTop: If true the Siri UI will be scrolled down so that the new view is at the top.
+ dialogPhase: Possible values are Completion, Reflection, Summary, Error, Clarification, and Acknowledgement */
- (void)sendTextSnippet:(NSString*)text temporary:(BOOL)temporary scrollToTop:(BOOL)toTop dialogPhase:(NSString*)phase;
+
+/* Create an editable dictionary representing a text snippet. In order to send this
+ to the user you must add it to an NSArray and use sendAddviews: */
-(NSMutableDictionary*)createTextSnippet:(NSString*)text;
+
+/* Send several views to the user at once */
- (void)sendAddViews:(NSArray*)views;
+
+/* Send several views to the user with control over the parameters */
- (void)sendAddViews:(NSArray*)views dialogPhase:(NSString*)dialogPhase scrollToTop:(BOOL)toTop temporary:(BOOL)temporary;
--(NSMutableDictionary*)createSnippet:(NSString*)snippetClass properties:(NSDictionary*)props;
+
+/* Create and immediately send a custom snippet with the specified properties. The snippet must
+ have been registered with the plugin manager */
- (void)sendCustomSnippet:(NSString*)snippetClass withProperties:(NSDictionary*)props;
+
+/* Create an editable dictionary representing a custom snippet. In order to send this
+ to the user you must add it to an NSArray and use sendAddViews: */
+-(NSMutableDictionary*)createSnippet:(NSString*)snippetClass properties:(NSDictionary*)props;
+
+/* Tell the session that you're done with this request and that it can end. You must do
+ this or Siri will timeout and display an error message.*/
- (void)sendRequestCompleted;
--(NSMutableDictionary*)createAssistantUtteranceView:(NSString*)text;
-- (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *info))completion;
+
+/* Retrieve the user's current location and then execute the code in the completion block
+ with the location info. The location info will be structured as follows:
+
+ NSDictionary *dict = @{@"latitude" : NSNumber,
+ @"longitude" : NSNumber,
+ @"horizontalAccuracy" : NSNumber,
+ @"verticalAccuracy" : NSNumber,
+ @"speed" : NSNumber,
+ @"course" : NSNumber,
+ @"timestamp" : NSDate} */
+- (void)getCurrentLocationWithCompletion:(void (^)(NSDictionary *locationInfo))completion;
+
@end
+/* None of your classes should need to conform to this protocol, but you will use it to register
+ your command and snippet classes when your principal class's (the one that conforms to APPlugin)
+ initWithSystem: method is called */
@protocol APPluginManager
@required
-/// Register a command class
--(BOOL)registerCommand:(Class)cls;
-/// Register a snippet class
--(BOOL)registerSnippet:(Class)cls;
+-(BOOL)registerCommand:(Class)commandClass;
+-(BOOL)registerSnippet:(Class)snippetClass;
@end
+/* Your custom snippet class (which should always be a subclass of UIViewController)
+ will need to conform to this protocol */
@protocol APPluginSnippet
@optional
-/// Initializes a snippet by properties
+/* If you use APSiriSession's sendCustomSnippet:withProperties: and want to be able
+ to access the properties in your snippet then you must implement this method. If you
+ don't implement this method then the snippet will be created with [yourSnippet init] */
-(id)initWithProperties:(NSDictionary*)props;
-/// Initializes a snippet by properties and system
--(id)initWithProperties:(NSDictionary*)props system:(id)system;
-/// Returns a view representing snippet, can be self if the conforming class is already UIView
+
@end
-
-/** Protocol specifying methods of an extension class handling commands.
- Classes conforming to this protocol are initialized just after loading bundle and will remain in memory.
- Don't forget you really should prefix your class with some shortcut, e.g. K3AAwesomeCommand!
- */
+/* This is where you will handle the user's query and determine if your plugin
+ should handle it. This "command" class should essentially be the brain of your plugin
+ and determine which snippet/s to show or action/s to take */
@protocol APPluginCommand
@required
+/* You should try to make this method run as quickly as possible, as this method will be called on
+ all installed plugins or until one returns YES. If you've determined that your plugin should handle
+ the user's query then do any time-intensive tasks (such as network calls) on another thread */
-(BOOL)handleSpeech:(NSString*)text withTokens:(NSSet*)tokens withSession:(id)session;
-@optional
--(id)initWithSystem:(id)manager;
--(void)assistantDismissed;
@end
-/// Protocol specifying methods of the extension's principal class
+/* Your principal/"main" class should conform to this protocol. */
@protocol APPlugin
@required
-/// The first method which is called on your class, system is where you register commands and snippets
--(id)initWithSystem:(id)system;
-@optional
--(void)assistantDismissed; //clean up
+/* When your plugin is initialized the plugin manager will call this method. This is
+ when you should register you command/s and snippet/s using the system's registerCommand: and
+ registerSnippet: methods, repsectively. */
+-(id)initWithPluginManager:(id)manager;
@end
#endif
diff --git a/spotifyPlugin/spotifyPlugin/Makefile b/spotifyPlugin/spotifyPlugin/Makefile
index 9a2774f..e86a617 100644
--- a/spotifyPlugin/spotifyPlugin/Makefile
+++ b/spotifyPlugin/spotifyPlugin/Makefile
@@ -1,9 +1,8 @@
include theos/makefiles/common.mk
-export THEOS_DEVICE_IP=192.168.1.17
export ARCHS = armv7 arm64
export TARGET = iphone:clang:latest:8.0
-export SDKVERSION=8.2
+export SDKVERSION=8.1
BUNDLE_NAME = spotifySiri
spotifySiri_BUNDLE_EXTENSION = assistantPlugin
diff --git a/spotifyPlugin/spotifyPlugin/Resources/Info.plist b/spotifyPlugin/spotifyPlugin/Resources/Info.plist
index 3665c33..13c0921 100644
--- a/spotifyPlugin/spotifyPlugin/Resources/Info.plist
+++ b/spotifyPlugin/spotifyPlugin/Resources/Info.plist
@@ -2,6 +2,10 @@
+ APPluginName
+ Spotify Control
+ APPluginAuthor
+ Zaid Elkurdi
CFBundleName
spotifySiri
CFBundleIdentifier
diff --git a/spotifyPlugin/spotifyPlugin/SpotifyPlayCommands.h b/spotifyPlugin/spotifyPlugin/SpotifyPlayCommands.h
new file mode 100644
index 0000000..dcbcded
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/SpotifyPlayCommands.h
@@ -0,0 +1,16 @@
+//
+// SpotifyPlayCommands.h
+// spotifyPlugin
+//
+// Created by Zaid Elkurdi on 3/30/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import
+#import "AssistantPlusHeaders.h"
+
+@interface SpotifyPlayCommands : NSObject
+
+- (BOOL)handleSpeech:(NSString *)text withTokens:(NSSet *)tokens withSession:(id)session;
+
+@end
diff --git a/spotifyPlugin/spotifyPlugin/SpotifyPlayCommands.m b/spotifyPlugin/spotifyPlugin/SpotifyPlayCommands.m
new file mode 100644
index 0000000..52fdfaf
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/SpotifyPlayCommands.m
@@ -0,0 +1,115 @@
+//
+// SpotifyPlayCommands.m
+// spotifyPlugin
+//
+// Created by Zaid Elkurdi on 3/30/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import "SpotifyPlayCommands.h"
+#import "AFNetworking/AFNetworking.h"
+
+@implementation SpotifyPlayCommands
+
+- (BOOL)handleSpeech:(NSString *)text withTokens:(NSSet *)tokens withSession:(id)session {
+ if ([tokens containsObject:@"play"] && [tokens containsObject:@"on"] && [tokens containsObject:@"spotify"]) {
+ NSRegularExpression *songAndArtistRegex = [NSRegularExpression regularExpressionWithPattern:@"(?:.*)Play (.*) by (.*) on Spotify" options:NSRegularExpressionCaseInsensitive error:nil];
+ NSRegularExpression *justSongRegex = [NSRegularExpression regularExpressionWithPattern:@"(?:.*)Play(.*)on Spotify" options:NSRegularExpressionCaseInsensitive error:nil];
+
+ NSArray *justSongMatches = [justSongRegex matchesInString:text options:0 range:NSMakeRange(0, [text length])];
+ NSArray *songAndArtistMatches = [songAndArtistRegex matchesInString:text options:0 range:NSMakeRange(0, [text length])];
+
+ NSString *artistName = nil;
+ NSString *songName = nil;
+
+ for (NSTextCheckingResult *match in songAndArtistMatches) {
+ if (match.numberOfRanges > 2) {
+ songName = [text substringWithRange:[match rangeAtIndex:1]];
+ artistName = [text substringWithRange:[match rangeAtIndex:2]];
+ }
+ }
+
+ if (!artistName || !songName) {
+ for (NSTextCheckingResult *match in justSongMatches) {
+ if (match.numberOfRanges > 1) {
+ songName = [text substringWithRange:[match rangeAtIndex:1]];
+ }
+ }
+ }
+
+ if (!songName || songName.length == 0) {
+ return NO;
+ }
+
+ [session sendTextSnippet:@"Searching..." temporary:YES scrollToTop:NO dialogPhase:@"Reflection"];
+
+ //Don't do network calls on Siri UI thread
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [self searchForSong:songName byArtist:artistName forSession:session];
+ });
+
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)searchForSong:(NSString*)songName byArtist:(NSString*)artistName forSession:(id)session {
+
+ AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
+ [manager GET:@"http://ws.spotify.com/search/1/track.json" parameters:@{@"q" : songName} success:^(AFHTTPRequestOperation *operation, id responseObject) {
+ NSArray *tracks = responseObject[@"tracks"];
+
+ if (!tracks || tracks.count == 0) {
+ [session sendTextSnippet:@"Sorry, I couldn't find anything" temporary:NO scrollToTop:NO dialogPhase:@"Completion"];
+ return;
+ }
+
+ NSString *href = nil;
+
+ //Search with artist name first
+ if (artistName) {
+ href = [self getSongForArtist:artistName fromSongs:tracks];
+ }
+
+ //If we couldn't find a song with this name by the artist, default to the
+ //top result for the song name
+ if (!artistName) {
+ NSDictionary *topResult = tracks[0];
+ href = topResult[@"href"];
+ }
+
+ //If we found the song, go to it.
+ if (href) {
+ [[UIApplication sharedApplication]openURL:[NSURL URLWithString:href]];
+ }
+
+ //End session
+ [session sendRequestCompleted];
+
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
+ [session sendTextSnippet:@"Sorry, I couldn't find that song" temporary:NO scrollToTop:YES dialogPhase:@"Completion"];
+ [session sendRequestCompleted];
+ }];
+}
+
+- (NSString*)getSongForArtist:(NSString*)artistName fromSongs:(NSArray*)songs {
+ for (NSDictionary *currSong in songs) {
+ NSArray *artistsInfo = currSong[@"artists"];
+ if (artistsInfo) {
+ for (NSDictionary *currArtist in artistsInfo) {
+ NSString *currName = currArtist[@"name"];
+ if ([artistName compare:currName options:NSCaseInsensitiveSearch] == NSOrderedSame) {
+ NSString *songHref = currSong[@"href"];
+ if (songHref) {
+ return songHref;
+ }
+ }
+ }
+ }
+ }
+ return nil;
+}
+
+
+@end
diff --git a/spotifyPlugin/spotifyPlugin/spotifyCommands.h b/spotifyPlugin/spotifyPlugin/SpotifySearchCommands.h
similarity index 84%
rename from spotifyPlugin/spotifyPlugin/spotifyCommands.h
rename to spotifyPlugin/spotifyPlugin/SpotifySearchCommands.h
index 98d1a21..e80a30d 100644
--- a/spotifyPlugin/spotifyPlugin/spotifyCommands.h
+++ b/spotifyPlugin/spotifyPlugin/SpotifySearchCommands.h
@@ -9,7 +9,7 @@
#import
#import "AssistantPlusHeaders.h"
-@interface spotifyCommands : NSObject
+@interface SpotifySearchCommands : NSObject
- (BOOL)handleSpeech:(NSString *)text withTokens:(NSSet *)tokens withSession:(id)session;
diff --git a/spotifyPlugin/spotifyPlugin/SpotifySearchCommands.m b/spotifyPlugin/spotifyPlugin/SpotifySearchCommands.m
new file mode 100644
index 0000000..7c08a1b
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/SpotifySearchCommands.m
@@ -0,0 +1,120 @@
+//
+// spotifyCommands.m
+// spotifyPlugin
+//
+// Created by Zaid Elkurdi on 3/28/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import "spotifySearchCommands.h"
+#import "AFNetworking/AFNetworking.h"
+
+@implementation SpotifySearchCommands
+
+- (BOOL)handleSpeech:(NSString *)text withTokens:(NSSet *)tokens withSession:(id)session {
+ //Do a quick/rough check first to see if we should do any more thorough processing
+ if ([tokens containsObject:@"search"] && [tokens containsObject:@"on"] && [tokens containsObject:@"spotify"]) {
+ /*Handle different permutations of query eg.
+ Hey Siri search for Taylor Swift on Spotify
+ Search for Drake on Spotify
+ Search Drake on Spotify
+ */
+
+ NSRegularExpression *queryRegex = [NSRegularExpression regularExpressionWithPattern:@"(?:.*)Search (?:for)?(.*) on Spotify" options:NSRegularExpressionCaseInsensitive error:nil];
+ NSArray *arrayOfAllMatches = [queryRegex matchesInString:text options:0 range:NSMakeRange(0, [text length])];
+
+ NSString *query = nil;
+ for (NSTextCheckingResult *match in arrayOfAllMatches) {
+ if (match.numberOfRanges > 1) {
+ query = [text substringWithRange:[match rangeAtIndex:1]];
+ }
+ }
+
+ if (!query || query.length == 0) {
+ return NO;
+ }
+
+ //Display a temporary message that will be replaced by the result. Notice that the dialogPhase is "Reflection".
+ [session sendTextSnippet:@"Searching..." temporary:YES scrollToTop:NO dialogPhase:@"Reflection"];
+
+ //Don't want to do the network calls on the Siri UI thread
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [self searchSpotifyForQuery:query forSession:session];
+ });
+
+ //We will be handling this request
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)searchSpotifyForQuery:(NSString*)query forSession:(id)session {
+ query = [query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ __block NSArray *trackResults = [NSArray array];
+ __block NSArray *albumResults = [NSArray array];
+ __block NSArray *artistResults = [NSArray array];
+
+ NSURL *trackURL = [NSURL URLWithString:[[NSString stringWithFormat:@"http://ws.spotify.com/search/1/track.json?q=%@", query] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ NSURL *artistURL = [NSURL URLWithString:[[NSString stringWithFormat:@"http://ws.spotify.com/search/1/artist.json?q=%@", query] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ NSURL *albumURL = [NSURL URLWithString:[[NSString stringWithFormat:@"http://ws.spotify.com/search/1/album.json?q=%@", query] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+
+ NSURLRequest *trackRequest = [NSURLRequest requestWithURL:trackURL];
+ AFHTTPRequestOperation *trackOperation = [[AFHTTPRequestOperation alloc] initWithRequest:trackRequest];
+ trackOperation.responseSerializer = [AFJSONResponseSerializer serializer];
+ [trackOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
+ trackResults = responseObject[@"tracks"] ? responseObject[@"tracks"] : trackResults;
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {}];
+
+ NSURLRequest *albumRequest = [NSURLRequest requestWithURL:albumURL];
+ AFHTTPRequestOperation *albumOperation = [[AFHTTPRequestOperation alloc] initWithRequest:albumRequest];
+ albumOperation.responseSerializer = [AFJSONResponseSerializer serializer];
+ [albumOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
+ albumResults = responseObject[@"albums"] ? responseObject[@"albums"] : albumResults;
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {}];
+
+ NSURLRequest *artistRequest = [NSURLRequest requestWithURL:artistURL];
+ AFHTTPRequestOperation *artistOperation = [[AFHTTPRequestOperation alloc] initWithRequest:artistRequest];
+ artistOperation.responseSerializer = [AFJSONResponseSerializer serializer];
+ [artistOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
+ artistResults = responseObject[@"artists"] ? responseObject[@"artists"] : artistResults;
+
+ if (trackResults.count + albumResults.count + artistResults.count == 0) {
+ NSString *errorMsg = [NSString stringWithFormat:@"Sorry, I wasn't able to find anything for '%@'", query];
+
+ //We didn't find any results so just display the error message. This will replace the "Searching..." message.
+ [session sendTextSnippet:errorMsg temporary:NO scrollToTop:NO dialogPhase:@"Completion"];
+ } else {
+ /* You could also send the views like this:
+ NSMutableDictionary *textSnippet = [session createTextSnippet:@"Here's what I found..."];
+ NSMutableDictionary *customSnippet = [session createSnippet:@"SpotifySearchResultsViewController" properties:@{@"tracks" : [trackResults subarrayWithRange:NSMakeRange(0, MIN(trackResults.count, 5))],
+ @"albums" : [albumResults subarrayWithRange:NSMakeRange(0, MIN(albumResults.count, 5))],
+ @"artists" : [artistResults subarrayWithRange:NSMakeRange(0, MIN(artistResults.count, 5))]}];
+ [session sendAddViews:@[textSnippet, customSnippet]]; */
+
+ //Display our custom snippet with the results. This will replace the "Searching..." message.
+ [session sendTextSnippet:@"Here's what I found..." temporary:NO scrollToTop:NO dialogPhase:@"Summary"];
+ [session sendCustomSnippet:@"SpotifySearchResultsViewController" withProperties:@{@"tracks" : [trackResults subarrayWithRange:NSMakeRange(0, MIN(trackResults.count, 5))],
+ @"albums" : [albumResults subarrayWithRange:NSMakeRange(0, MIN(albumResults.count, 5))],
+ @"artists" : [artistResults subarrayWithRange:NSMakeRange(0, MIN(artistResults.count, 5))]}];
+ }
+
+ //Make sure to tell the APSiriSession that we're done.
+ [session sendRequestCompleted];
+
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
+ [session sendRequestCompleted];
+ }];
+
+
+ NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
+
+ [albumOperation addDependency:trackOperation];
+ [artistOperation addDependency:albumOperation];
+
+ [operationQueue addOperation:trackOperation];
+ [operationQueue addOperation:albumOperation];
+ [operationQueue addOperation:artistOperation];
+}
+
+@end
diff --git a/spotifyPlugin/spotifyPlugin/spotifySongListViewController.h b/spotifyPlugin/spotifyPlugin/SpotifySearchResultsViewController.h
similarity index 62%
rename from spotifyPlugin/spotifyPlugin/spotifySongListViewController.h
rename to spotifyPlugin/spotifyPlugin/SpotifySearchResultsViewController.h
index 4a1b66f..2052db4 100644
--- a/spotifyPlugin/spotifyPlugin/spotifySongListViewController.h
+++ b/spotifyPlugin/spotifyPlugin/SpotifySearchResultsViewController.h
@@ -9,8 +9,8 @@
#import
#import "AssistantPlusHeaders.h"
-@interface spotifySongListViewController : UIViewController
+//Your UIViewController subclass must conform to APPluginSnippet
+@interface SpotifySearchResultsViewController : UIViewController
@property (strong, nonatomic) UITableView *songsTable;
-(id)initWithProperties:(NSDictionary*)props;
--(double)desiredHeightForWidth:(double)height;
@end
diff --git a/spotifyPlugin/spotifyPlugin/SpotifySearchResultsViewController.m b/spotifyPlugin/spotifyPlugin/SpotifySearchResultsViewController.m
new file mode 100644
index 0000000..1f2a180
--- /dev/null
+++ b/spotifyPlugin/spotifyPlugin/SpotifySearchResultsViewController.m
@@ -0,0 +1,148 @@
+//
+// spotifySongListViewController.m
+// spotifyPlugin
+//
+// Created by Zaid Elkurdi on 3/28/15.
+// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
+//
+
+#import "SpotifySearchResultsViewController.h"
+#import "spotifySongTableViewCell.h"
+
+@implementation SpotifySearchResultsViewController {
+ NSArray *artistResults;
+ NSArray *trackResults;
+ NSArray *albumResults;
+}
+
+-(id)initWithProperties:(NSDictionary*)props {
+ if (self = [super init]) {
+ artistResults = [props objectForKey:@"artists"];
+ trackResults = [props objectForKey:@"tracks"];
+ albumResults = [props objectForKey:@"albums"];
+ }
+
+ return self;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.songsTable = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleGrouped];
+ self.songsTable.delegate = self;
+ self.songsTable.dataSource = self;
+ self.songsTable.backgroundColor = [UIColor clearColor];
+ self.songsTable.layoutMargins = UIEdgeInsetsZero;
+ self.songsTable.scrollEnabled = NO;
+
+ [self.songsTable layoutIfNeeded];
+ CGRect newFrame = self.songsTable.frame;
+ newFrame.size.height = self.songsTable.contentSize.height;
+
+ self.view.frame = newFrame;
+ self.songsTable.frame = self.view.frame;
+
+ [self.view addSubview:self.songsTable];
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+}
+
+#pragma mark - UITableViewDelegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ NSDictionary *selectedEntity;
+ if (indexPath.section == 0) {
+ selectedEntity = [artistResults objectAtIndex:indexPath.row];
+ } else if (indexPath.section == 1) {
+ selectedEntity = [trackResults objectAtIndex:indexPath.row];
+ } else {
+ selectedEntity = [albumResults objectAtIndex:indexPath.row];
+ }
+
+ NSString *href = selectedEntity[@"href"];
+ [[UIApplication sharedApplication]openURL:[NSURL URLWithString:href]];
+}
+
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return indexPath.section == 0 ? 50 : 77;
+}
+
+- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
+ UITableViewHeaderFooterView *header = (UITableViewHeaderFooterView*)view;
+ header.textLabel.textColor = [UIColor whiteColor];
+}
+
+#pragma mark - UITableViewDataSource
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+ return 3;
+}
+
+- (NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+ NSString *title = nil;
+ switch (section) {
+ case 0:
+ title = @"Artists";
+ break;
+ case 1:
+ title = @"Tracks";
+ break;
+ case 2:
+ title = @"Albums";
+ default:
+ break;
+ }
+
+ return title;
+}
+
+- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ SpotifySongTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"songCell"];
+
+ if (!cell) {
+ //The code is going to be run by the Siri UI process, so we can't just use [NSBundle mainBundle]
+ NSBundle *pluginBundle = [NSBundle bundleWithPath:@"/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin"];
+ [tableView registerNib:[UINib nibWithNibName:@"SpotifySongTableViewCell" bundle:pluginBundle] forCellReuseIdentifier:@"songCell"];
+ cell = [tableView dequeueReusableCellWithIdentifier:@"songCell"];
+ }
+
+ cell.backgroundColor = [UIColor clearColor];
+
+ NSDictionary *currEntity;
+ if (indexPath.section == 0) {
+ currEntity = [artistResults objectAtIndex:indexPath.row];
+ cell.songDetailLabel.text = @"";
+ } else if (indexPath.section == 1) {
+ currEntity = [trackResults objectAtIndex:indexPath.row];
+ cell.songDetailLabel.text = [NSString stringWithFormat:@"%@ - %@", currEntity[@"artists"][0][@"name"], currEntity[@"album"][@"name"]];
+ } else {
+ currEntity = [albumResults objectAtIndex:indexPath.row];
+ cell.songDetailLabel.text = [NSString stringWithFormat:@"%@", currEntity[@"artists"][0][@"name"]];
+ }
+
+ cell.songTitleLabel.text = currEntity[@"name"];
+
+ return cell;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ NSInteger numRows = 0;
+ switch (section) {
+ case 0:
+ numRows = MIN(artistResults.count, 5);
+ break;
+ case 1:
+ numRows = MIN(trackResults.count, 5);
+ break;
+ case 2:
+ numRows = MIN(albumResults.count, 5);
+ default:
+ break;
+ }
+
+ return numRows;
+}
+
+@end
diff --git a/spotifyPlugin/spotifyPlugin/SpotifySongTableViewCell.xib b/spotifyPlugin/spotifyPlugin/SpotifySongTableViewCell.xib
index b3d9cf3..d5c10ea 100644
--- a/spotifyPlugin/spotifyPlugin/SpotifySongTableViewCell.xib
+++ b/spotifyPlugin/spotifyPlugin/SpotifySongTableViewCell.xib
@@ -1,7 +1,7 @@
-
+
-
+
diff --git a/spotifyPlugin/spotifyPlugin/_/DEBIAN/control b/spotifyPlugin/spotifyPlugin/_/DEBIAN/control
index 4e11b2e..80f4892 100644
--- a/spotifyPlugin/spotifyPlugin/_/DEBIAN/control
+++ b/spotifyPlugin/spotifyPlugin/_/DEBIAN/control
@@ -1,10 +1,10 @@
-Package: com.assistantplus.customreply
-Name: customreply
-Depends: mobilesubstrate
+Package: com.assistantplus.spotifycontrols
+Name: spotifySiriControls
+Depends: mobilesubstrate, com.zaid.assistant+
Architecture: iphoneos-arm
-Description: An awesome MobileSubstrate tweak!
+Description: Control Spotify using Siri! Currently spotifySiriControls supports searching for tracks, artists, and albums using commands like, “Search for Drake on Spotify”, “Search Taylor Swift on Spotify”, “Play Books by Swiss Lips on Spotify”, and “Play Paris on Spotify”.
Maintainer: Zaid Elkurdi
Author: Zaid Elkurdi
Section: Tweaks
-Version: 0.0.1-25
-Installed-Size: 548
+Version: 1.0.0-6
+Installed-Size: 616
diff --git a/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/Info.plist b/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/Info.plist
index 3665c33..13c0921 100644
--- a/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/Info.plist
+++ b/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/Info.plist
@@ -2,6 +2,10 @@
+ APPluginName
+ Spotify Control
+ APPluginAuthor
+ Zaid Elkurdi
CFBundleName
spotifySiri
CFBundleIdentifier
diff --git a/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/spotifySiri b/spotifyPlugin/spotifyPlugin/_/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin/spotifySiri
index c62e476..7463cf5 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/control b/spotifyPlugin/spotifyPlugin/control
index 593c5c8..003d279 100644
--- a/spotifyPlugin/spotifyPlugin/control
+++ b/spotifyPlugin/spotifyPlugin/control
@@ -1,9 +1,9 @@
-Package: com.assistantplus.customreply
-Name: customreply
-Depends: mobilesubstrate
-Version: 0.0.1
+Package: com.assistantplus.spotifycontrols
+Name: spotifySiriControls
+Depends: mobilesubstrate, com.zaid.assistant+
+Version: 1.0.0
Architecture: iphoneos-arm
-Description: An awesome MobileSubstrate tweak!
+Description: Control Spotify using Siri! Currently spotifySiriControls supports searching for tracks, artists, and albums using commands like, “Search for Drake on Spotify”, “Search Taylor Swift on Spotify”, “Play Books by Swiss Lips on Spotify”, and “Play Paris on Spotify”.
Maintainer: Zaid Elkurdi
Author: Zaid Elkurdi
Section: Tweaks
diff --git a/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/Info.plist b/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/Info.plist
index 3665c33..13c0921 100644
--- a/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/Info.plist
+++ b/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/Info.plist
@@ -2,6 +2,10 @@
+ APPluginName
+ Spotify Control
+ APPluginAuthor
+ Zaid Elkurdi
CFBundleName
spotifySiri
CFBundleIdentifier
diff --git a/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/spotifySiri b/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/spotifySiri
index c62e476..7463cf5 100755
Binary files a/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/spotifySiri and b/spotifyPlugin/spotifyPlugin/obj/spotifySiri.assistantPlugin/spotifySiri differ
diff --git a/spotifyPlugin/spotifyPlugin/spotifyCommands.m b/spotifyPlugin/spotifyPlugin/spotifyCommands.m
deleted file mode 100644
index 3fb6591..0000000
--- a/spotifyPlugin/spotifyPlugin/spotifyCommands.m
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// spotifyCommands.m
-// spotifyPlugin
-//
-// Created by Zaid Elkurdi on 3/28/15.
-// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
-//
-
-#import "spotifyCommands.h"
-#import "AFNetworking/AFNetworking.h"
-
-@implementation spotifyCommands
-
-- (BOOL)handleSpeech:(NSString *)text withTokens:(NSSet *)tokens withSession:(id)session {
- if ([tokens containsObject:@"play"] && [tokens containsObject:@"on"] && [tokens containsObject:@"spotify"]) {
-
- NSRegularExpression *justSongRegex = [NSRegularExpression regularExpressionWithPattern:@"Play(.*)on Spotify" options:NSRegularExpressionCaseInsensitive error:nil];
-
- NSArray *arrayOfAllMatches = [justSongRegex matchesInString:text options:0 range:NSMakeRange(0, [text length])];
-
- NSString *songName = nil;
- for (NSTextCheckingResult *match in arrayOfAllMatches) {
- if (match.numberOfRanges > 1) {
- songName = [text substringWithRange:[match rangeAtIndex:1]];
- }
- }
-
- if (!songName || songName.length == 0) {
- return NO;
- }
-
- NSLog(@" Extracted Song: %@",songName);
- [session sendTextSnippet:@"Here's what I found..." temporary:NO scrollToTop:NO dialogPhase:@"Completion"];
-
- AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
- [manager GET:@"http://ws.spotify.com/search/1/track.json" parameters:@{@"q" : songName} success:^(AFHTTPRequestOperation *operation, id responseObject) {
- NSLog(@"JSON: %@", responseObject);
- [session sendCustomSnippet:@"spotifySongListViewController" withProperties:@{@"songs" : responseObject[@"tracks"]}];
- [session sendRequestCompleted];
- } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
- [session sendTextSnippet:@"Sorry, I couldn't find that song" temporary:NO scrollToTop:YES dialogPhase:@"Completion"];
- [session sendRequestCompleted];
- }];
- }
-
- return YES;
-}
-
-@end
diff --git a/spotifyPlugin/spotifyPlugin/spotifySiri.h b/spotifyPlugin/spotifyPlugin/spotifySiri.h
index 983c705..bd84d5d 100644
--- a/spotifyPlugin/spotifyPlugin/spotifySiri.h
+++ b/spotifyPlugin/spotifyPlugin/spotifySiri.h
@@ -10,5 +10,5 @@
#import "AssistantPlusHeaders.h"
@interface spotifySiri : NSObject
--(id)initWithSystem:(id)system;
+-(id)initWithPluginManager:(id)manager;
@end
diff --git a/spotifyPlugin/spotifyPlugin/spotifySiri.m b/spotifyPlugin/spotifyPlugin/spotifySiri.m
index b9f079a..0861839 100644
--- a/spotifyPlugin/spotifyPlugin/spotifySiri.m
+++ b/spotifyPlugin/spotifyPlugin/spotifySiri.m
@@ -7,15 +7,18 @@
//
#import "spotifySiri.h"
-#import "spotifyCommands.h"
-#import "spotifySongListViewController.h"
+#import "spotifySearchCommands.h"
+#import "SpotifyPlayCommands.h"
+#import "SpotifySearchResultsViewController.h"
@implementation spotifySiri
--(id)initWithSystem:(id)system {
+-(id)initWithPluginManager:(id)manager {
if (self = [super init]) {
- [system registerCommand:[spotifyCommands class]];
- [system registerSnippet:[spotifySongListViewController class]];
+ //Register the commands and snippet with the plugin manager
+ [manager registerCommand:[SpotifySearchCommands class]];
+ [manager registerCommand:[SpotifyPlayCommands class]];
+ [manager registerSnippet:[SpotifySearchResultsViewController class]];
}
return self;
}
diff --git a/spotifyPlugin/spotifyPlugin/spotifySongListViewController.m b/spotifyPlugin/spotifyPlugin/spotifySongListViewController.m
deleted file mode 100644
index 981661d..0000000
--- a/spotifyPlugin/spotifyPlugin/spotifySongListViewController.m
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-// spotifySongListViewController.m
-// spotifyPlugin
-//
-// Created by Zaid Elkurdi on 3/28/15.
-// Copyright (c) 2015 Zaid Elkurdi. All rights reserved.
-//
-
-#import "spotifySongListViewController.h"
-#import "spotifySongTableViewCell.h"
-
-@implementation spotifySongListViewController {
- NSArray *songsToDisplay;
-}
-
--(id)initWithProperties:(NSDictionary*)props {
- if (self = [super init]) {
- songsToDisplay = [props objectForKey:@"songs"];
- }
- return self;
-}
-
-- (void)viewDidLoad {
- [super viewDidLoad];
- self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, (77*MIN(songsToDisplay.count, 10))+40);
- self.songsTable = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
- self.songsTable.delegate = self;
- self.songsTable.dataSource = self;
- self.songsTable.backgroundColor = [UIColor clearColor];
- self.songsTable.layoutMargins = UIEdgeInsetsZero;
- [self.view addSubview:self.songsTable];
-}
-
--(double)desiredHeightForWidth:(double)height {
- return self.view.frame.size.height;
-}
-
-- (void)didReceiveMemoryWarning {
- [super didReceiveMemoryWarning];
-}
-
-#pragma mark - UITableViewDelegate
-
-- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- NSDictionary *selectedSong = [songsToDisplay objectAtIndex:indexPath.row];
- NSString *href = selectedSong[@"href"];
- [[UIApplication sharedApplication]openURL:[NSURL URLWithString:href]];
-}
-
-- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
- return 77;
-}
-
-#pragma mark - UITableViewDataSource
-
-- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- SpotifySongTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"songCell"];
-
- if (!cell) {
- NSBundle *pluginBundle = [NSBundle bundleWithPath:@"/Library/AssistantPlusPlugins/spotifySiri.assistantPlugin"];
- [tableView registerNib:[UINib nibWithNibName:@"SpotifySongTableViewCell" bundle:pluginBundle] forCellReuseIdentifier:@"songCell"];
- cell = [tableView dequeueReusableCellWithIdentifier:@"songCell"];
- }
- cell.backgroundColor = [UIColor clearColor];
- NSDictionary *currSong = [songsToDisplay objectAtIndex:indexPath.row];
- NSLog(@"Curr song: %@", currSong);
- cell.songTitleLabel.text = currSong[@"name"];
- cell.songDetailLabel.text = [NSString stringWithFormat:@"%@ - %@", currSong[@"artists"][0][@"name"], currSong[@"album"][@"name"]];
- cell.layoutMargins = UIEdgeInsetsZero;
- return cell;
-}
-
-- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- return MIN(10, songsToDisplay.count);
-}
-
-@end