diff --git a/.gitmodules b/.gitmodules
index 34a6177f..8826ade3 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,6 @@
[submodule "External/iCloudStoreManager"]
path = External/iCloudStoreManager
url = git://github.com/alekseyn/iCloudStoreManager.git
+[submodule "External/apptentive"]
+ path = External/apptentive
+ url = git://github.com/apptentive/apptentive-ios.git
diff --git a/Apptentive/Apptentive.plist b/Apptentive/Apptentive.plist
new file mode 100644
index 00000000..efa55526
--- /dev/null
+++ b/Apptentive/Apptentive.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ API Key
+
+
+
diff --git a/External/Pearl b/External/Pearl
index fea9295f..f92b0b14 160000
--- a/External/Pearl
+++ b/External/Pearl
@@ -1 +1 @@
-Subproject commit fea9295f84e16534f70b616e622946bdd518f27e
+Subproject commit f92b0b1490e483d1bd4b8b6d1b9482c7ecfd4e44
diff --git a/External/apptentive b/External/apptentive
new file mode 160000
index 00000000..3b6635e1
--- /dev/null
+++ b/External/apptentive
@@ -0,0 +1 @@
+Subproject commit 3b6635e131d19f049fec1fd943afd89855185d39
diff --git a/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword-iOS.xcodeproj/project.pbxproj
index 990a4420..9595b487 100644
--- a/MasterPassword-iOS.xcodeproj/project.pbxproj
+++ b/MasterPassword-iOS.xcodeproj/project.pbxproj
@@ -74,6 +74,10 @@
DA95D5F614DF0B9F008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CC14DF0691008D1B94 /* IASKPSTextFieldSpecifierViewCell.xib */; };
DA95D5F714DF0B9F008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CD14DF0691008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib */; };
DA95D5F814DF0B9F008D1B94 /* IASKSpecifierValuesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA95D5CE14DF0691008D1B94 /* IASKSpecifierValuesView.xib */; };
+ DAAC35DB156BD62F00C5FD93 /* libApptentiveConnect.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAAC35D2156BD51600C5FD93 /* libApptentiveConnect.a */; };
+ DAAC35DC156BD66800C5FD93 /* ApptentiveResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DAAC35D6156BD51600C5FD93 /* ApptentiveResources.bundle */; };
+ DAAC35DE156BD77D00C5FD93 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */; };
+ DAAC35E4156BDBA700C5FD93 /* Apptentive.plist in Resources */ = {isa = PBXBuildFile; fileRef = DAAC35E2156BDBA700C5FD93 /* Apptentive.plist */; };
DAB8D45D15036BCF00CED3BC /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */; };
DAB8D45E15036BCF00CED3BC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D43F15036BCF00CED3BC /* InfoPlist.strings */; };
DAB8D45F15036BCF00CED3BC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D44115036BCF00CED3BC /* main.m */; };
@@ -858,6 +862,41 @@
remoteGlobalIDString = DA67FE5D14E4834300DB7CC9;
remoteInfo = util;
};
+ DAAC35D1156BD51600C5FD93 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 496DC37F1333D35600743F65;
+ remoteInfo = ApptentiveConnect;
+ };
+ DAAC35D3156BD51600C5FD93 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 496DC38C1333D35600743F65;
+ remoteInfo = ApptentiveConnectTests;
+ };
+ DAAC35D5156BD51600C5FD93 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 49D1118C13341A7C00603373;
+ remoteInfo = ApptentiveResources;
+ };
+ DAAC35D7156BD61D00C5FD93 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
+ proxyType = 1;
+ remoteGlobalIDString = 496DC37E1333D35600743F65;
+ remoteInfo = ApptentiveConnect;
+ };
+ DAAC35D9156BD61D00C5FD93 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
+ proxyType = 1;
+ remoteGlobalIDString = 49D1118B13341A7C00603373;
+ remoteInfo = ApptentiveResources;
+ };
DAC63283148681200075AEA5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA5BFA3B147E415C00F98B1E /* Project object */;
@@ -943,6 +982,11 @@
DA95D5CD14DF0691008D1B94 /* IASKPSToggleSwitchSpecifierViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IASKPSToggleSwitchSpecifierViewCell.xib; sourceTree = ""; };
DA95D5CE14DF0691008D1B94 /* IASKSpecifierValuesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = IASKSpecifierValuesView.xib; sourceTree = ""; };
DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
+ DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ApptentiveConnect.xcodeproj; path = External/apptentive/ApptentiveConnect/ApptentiveConnect.xcodeproj; sourceTree = ""; };
+ DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
+ DAAC35E2156BDBA700C5FD93 /* Apptentive.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Apptentive.plist; sourceTree = ""; };
+ DAAC35E5156BDBC100C5FD93 /* ATConnect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ATConnect.h; path = ../External/apptentive/ApptentiveConnect/source/ATConnect.h; sourceTree = ""; };
+ DAAC35E6156BDF0E00C5FD93 /* ATAppRatingFlow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ATAppRatingFlow.h; path = "../External/apptentive/ApptentiveConnect/source/Rating Flow/ATAppRatingFlow.h"; sourceTree = ""; };
DAB8D43D15036BCF00CED3BC /* MasterPassword.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MasterPassword.xcdatamodel; sourceTree = ""; };
DAB8D44015036BCF00CED3BC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
DAB8D44115036BCF00CED3BC /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
@@ -1731,6 +1775,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ DAAC35DE156BD77D00C5FD93 /* CoreTelephony.framework in Frameworks */,
+ DAAC35DB156BD62F00C5FD93 /* libApptentiveConnect.a in Frameworks */,
DA44260A1557D9E40052177D /* libiCloudStoreManager.a in Frameworks */,
DAD312C21552A22700A3F9ED /* libsqlite3.dylib in Frameworks */,
DAD312BF1552A1BD00A3F9ED /* libLocalytics.a in Frameworks */,
@@ -1810,6 +1856,7 @@
DA5BFA39147E415C00F98B1E = {
isa = PBXGroup;
children = (
+ DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */,
DA79A9BD1557DDC700BAA07A /* scrypt.xcodeproj */,
DA5BFA50147E415C00F98B1E /* MasterPassword */,
DAB8D46F15036BF600CED3BC /* Resources */,
@@ -1821,6 +1868,7 @@
DAD3125E15528C9C00A3F9ED /* Crashlytics */,
DAD3126115528C9C00A3F9ED /* TestFlight */,
DAD3127315528CD200A3F9ED /* Localytics */,
+ DAAC35E0156BDBA700C5FD93 /* Apptentive */,
DA4425D71557BF260052177D /* iCloudStoreManager */,
DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */,
@@ -1844,6 +1892,7 @@
DA5BFA47147E415C00F98B1E /* Frameworks */ = {
isa = PBXGroup;
children = (
+ DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */,
DAD312C01552A20800A3F9ED /* libsqlite3.dylib */,
DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */,
DABB981515100B4000B05417 /* SystemConfiguration.framework */,
@@ -1973,6 +2022,26 @@
path = External/InAppSettingsKit/InAppSettingsKit/Xibs;
sourceTree = SOURCE_ROOT;
};
+ DAAC35C9156BD51500C5FD93 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ DAAC35D2156BD51600C5FD93 /* libApptentiveConnect.a */,
+ DAAC35D4156BD51600C5FD93 /* ApptentiveConnectTests.octest */,
+ DAAC35D6156BD51600C5FD93 /* ApptentiveResources.bundle */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ DAAC35E0156BDBA700C5FD93 /* Apptentive */ = {
+ isa = PBXGroup;
+ children = (
+ DAAC35E6156BDF0E00C5FD93 /* ATAppRatingFlow.h */,
+ DAAC35E5156BDBC100C5FD93 /* ATConnect.h */,
+ DAAC35E2156BDBA700C5FD93 /* Apptentive.plist */,
+ );
+ path = Apptentive;
+ sourceTree = "";
+ };
DAB8D43E15036BCF00CED3BC /* iOS */ = {
isa = PBXGroup;
children = (
@@ -3073,6 +3142,8 @@
buildRules = (
);
dependencies = (
+ DAAC35D8156BD61D00C5FD93 /* PBXTargetDependency */,
+ DAAC35DA156BD61D00C5FD93 /* PBXTargetDependency */,
);
name = MasterPassword;
productName = MasterPassword;
@@ -3189,6 +3260,10 @@
productRefGroup = DA5BFA45147E415C00F98B1E /* Products */;
projectDirPath = "";
projectReferences = (
+ {
+ ProductGroup = DAAC35C9156BD51500C5FD93 /* Products */;
+ ProjectRef = DAAC35C8156BD51500C5FD93 /* ApptentiveConnect.xcodeproj */;
+ },
{
ProductGroup = DA79A9BE1557DDC700BAA07A /* Products */;
ProjectRef = DA79A9BD1557DDC700BAA07A /* scrypt.xcodeproj */;
@@ -3229,6 +3304,27 @@
remoteRef = DA79A9D11557DDC800BAA07A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
+ DAAC35D2156BD51600C5FD93 /* libApptentiveConnect.a */ = {
+ isa = PBXReferenceProxy;
+ fileType = archive.ar;
+ path = libApptentiveConnect.a;
+ remoteRef = DAAC35D1156BD51600C5FD93 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ DAAC35D4156BD51600C5FD93 /* ApptentiveConnectTests.octest */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.cfbundle;
+ path = ApptentiveConnectTests.octest;
+ remoteRef = DAAC35D3156BD51600C5FD93 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ DAAC35D6156BD51600C5FD93 /* ApptentiveResources.bundle */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.cfbundle;
+ path = ApptentiveResources.bundle;
+ remoteRef = DAAC35D5156BD51600C5FD93 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@@ -3237,6 +3333,7 @@
buildActionMask = 2147483647;
files = (
DAB8D97C1503718B00CED3BC /* jquery-1.6.1.min.js in Resources */,
+ DAAC35DC156BD66800C5FD93 /* ApptentiveResources.bundle in Resources */,
DA95D5F314DF0B9B008D1B94 /* IASKAppSettingsView.xib in Resources */,
DA95D5F414DF0B9F008D1B94 /* IASKAppSettingsWebView.xib in Resources */,
DA95D5F514DF0B9F008D1B94 /* IASKPSSliderSpecifierViewCell.xib in Resources */,
@@ -3897,6 +3994,7 @@
DA0A1D1215690AD40092735D /* tip_arrow_wood.png in Resources */,
DA0A1D1515690AF40092735D /* Icon-72@2x.png in Resources */,
DA0A1D1615690AF40092735D /* Icon-Small-50@2x.png in Resources */,
+ DAAC35E4156BDBA700C5FD93 /* Apptentive.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4065,6 +4163,16 @@
name = "Makefile-scrypt";
targetProxy = DA4DA1D71564470200F6F596 /* PBXContainerItemProxy */;
};
+ DAAC35D8156BD61D00C5FD93 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ name = ApptentiveConnect;
+ targetProxy = DAAC35D7156BD61D00C5FD93 /* PBXContainerItemProxy */;
+ };
+ DAAC35DA156BD61D00C5FD93 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ name = ApptentiveResources;
+ targetProxy = DAAC35D9156BD61D00C5FD93 /* PBXContainerItemProxy */;
+ };
DAC63284148681200075AEA5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DAC6325C1486805C0075AEA5 /* uicolor-utilities */;
diff --git a/MasterPassword/iOS/MPAppDelegate.m b/MasterPassword/iOS/MPAppDelegate.m
index 6128bc7f..c208cba5 100644
--- a/MasterPassword/iOS/MPAppDelegate.m
+++ b/MasterPassword/iOS/MPAppDelegate.m
@@ -15,6 +15,8 @@
#import "LocalyticsSession.h"
#import "TestFlight.h"
#import
+#import "ATConnect.h"
+#import "ATAppRatingFlow.h"
@interface MPAppDelegate ()
@@ -24,6 +26,9 @@
- (NSString *)crashlyticsInfo;
- (NSString *)crashlyticsAPIKey;
+- (NSString *)apptentiveInfo;
+- (NSString *)apptentiveAPIKey;
+
- (NSString *)localyticsInfo;
- (NSString *)localyticsKey;
@@ -143,8 +148,8 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
-#ifndef DEBUG
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+#ifndef DEBUG
@try {
NSString *testFlightToken = [self testFlightToken];
if ([testFlightToken length]) {
@@ -200,9 +205,22 @@
@catch (NSException *exception) {
err(@"Localytics exception: %@", exception);
}
- });
#endif
-
+ });
+
+ @try {
+ NSString *apptentiveAPIKey = [self apptentiveAPIKey];
+ if ([apptentiveAPIKey length]) {
+ dbg(@"Initializing Apptentive");
+ ATConnect *connection = [ATConnect sharedConnection];
+ connection.shouldTakeScreenshot = NO;
+ connection.apiKey = apptentiveAPIKey;
+ }
+ }
+ @catch (NSException *exception) {
+ err(@"Apptentive: %@", exception);
+ }
+
UIImage *navBarImage = [[UIImage imageNamed:@"ui_navbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsLandscapePhone];
@@ -270,11 +288,19 @@
@"https://youtrack.lyndir.com\n"
viewStyle:UIAlertViewStyleDefault tappedButtonBlock:nil
cancelTitle:nil otherTitles:[PearlStrings get].commonButtonOkay, nil];
+#else
+ @try {
+ ATAppRatingFlow *sharedFlow = [ATAppRatingFlow sharedRatingFlowWithAppID:[PearlConfig get].iTunesID];
+ [sharedFlow appDidLaunch:YES viewController:self.navigationController];
+ }
+ @catch (NSException *exception) {
+ err(@"Apptentive: %@", exception);
+ }
#endif
[[UIApplication sharedApplication] setStatusBarHidden:NO
withAnimation:UIStatusBarAnimationSlide];
-
+
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@@ -351,6 +377,11 @@
}
[TestFlight passCheckpoint:MPTestFlightCheckpointActivated];
+
+ ATAppRatingFlow *sharedFlow = [ATAppRatingFlow sharedRatingFlowWithAppID:[PearlConfig get].iTunesID];
+ [sharedFlow appDidEnterForeground:YES viewController:self.navigationController];
+
+ [super applicationDidBecomeActive:application];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
@@ -502,6 +533,25 @@
}
+#pragma mark - Apptentive
+
+
+- (NSDictionary *)apptentiveInfo {
+
+ static NSDictionary *apptentiveInfo = nil;
+ if (apptentiveInfo == nil)
+ apptentiveInfo = [[NSDictionary alloc] initWithContentsOfURL:
+ [[NSBundle mainBundle] URLForResource:@"Apptentive" withExtension:@"plist"]];
+
+ return apptentiveInfo;
+}
+
+- (NSString *)apptentiveAPIKey {
+
+ return NullToNil([[self apptentiveInfo] valueForKeyPath:@"API Key"]);
+}
+
+
#pragma mark - Localytics
diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m
index 3e740180..3b8d4a28 100644
--- a/MasterPassword/iOS/MPMainViewController.m
+++ b/MasterPassword/iOS/MPMainViewController.m
@@ -13,6 +13,7 @@
#import "MPElementGeneratedEntity.h"
#import "MPElementStoredEntity.h"
#import "IASKAppSettingsViewController.h"
+#import "ATConnect.h"
#import
@@ -393,36 +394,43 @@
return;
switch (buttonIndex - [sheet firstOtherButtonIndex]) {
- case 0:
+ case 0: {
[self toggleHelpAnimated:YES];
break;
+ }
case 1: {
[self setHelpChapter:@"faq"];
[self setHelpHidden:NO animated:YES];
break;
}
- case 2:
+ case 2: {
[[MPAppDelegate get] showGuide];
break;
- case 3:
- {
+ }
+ case 3: {
IASKAppSettingsViewController *settingsVC = [IASKAppSettingsViewController new];
settingsVC.delegate = self;
[self.navigationController pushViewController:settingsVC animated:YES];
break;
}
- case 4:
+ case 4: {
[[MPAppDelegate get] export];
break;
+ }
#ifdef ADHOC
- case 5:
+ case 5: {
[TestFlight openFeedbackView];
break;
- case 6:
+ }
+ case 6: {
#else
- case 5:
+ case 5: {
+ ATConnect *connection = [ATConnect sharedConnection];
+ [connection presentFeedbackControllerFromViewController:self];
+ break;
+ }
+ case 6: {
#endif
- {
[[MPAppDelegate get] signOut:self];
[[MPAppDelegate get] loadKey:YES];
break;
@@ -433,9 +441,7 @@
} cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil
otherTitles:
[self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Settings", @"Export",
-#ifdef ADHOC
@"Feedback",
-#endif
@"Sign Out",
nil];
}