From 09217961368d5ff65fe7563cd78ec5fda444aba4 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Thu, 5 Sep 2013 23:52:12 -0400 Subject: [PATCH] Log VC, iCloud toggling, iOS 7 fixes. [UPDATED] Show users and sites in cloud stores of log VC. [ADDED] Ability to turn off iCloud if corruption happens. [ADDED] When switching iCloud on/off, provide user the option to migrate his current sites. [ADDED] Ability to get to settings & logs from unlock VC. --- External/InAppSettingsKit | 2 +- External/UbiquityStoreManager | 2 +- .../ObjC/iOS/MPLogsViewController.m | 81 +++++-- .../ObjC/iOS/MPSetupViewController.m | 2 +- .../ObjC/iOS/MPUnlockViewController.h | 17 +- .../ObjC/iOS/MPUnlockViewController.m | 91 +++++--- MasterPassword/ObjC/iOS/MPiOSAppDelegate.m | 199 +++++++++++------- MasterPassword/ObjC/iOS/MPiOSConfig.h | 1 + MasterPassword/ObjC/iOS/MPiOSConfig.m | 5 +- .../ObjC/iOS/MainStoryboard_iPhone.storyboard | 38 +++- .../project.pbxproj | 4 + .../ObjC/iOS/Settings.bundle/Root.plist | 182 ++++++++-------- 12 files changed, 387 insertions(+), 237 deletions(-) diff --git a/External/InAppSettingsKit b/External/InAppSettingsKit index 3ad87357..fcc72db0 160000 --- a/External/InAppSettingsKit +++ b/External/InAppSettingsKit @@ -1 +1 @@ -Subproject commit 3ad873577684c1e98c11cc081200e9d0402ad3b7 +Subproject commit fcc72db0d54cd181f27b71e81901fc66958d71bf diff --git a/External/UbiquityStoreManager b/External/UbiquityStoreManager index 77327f4a..ebc094f4 160000 --- a/External/UbiquityStoreManager +++ b/External/UbiquityStoreManager @@ -1 +1 @@ -Subproject commit 77327f4adc7ca9dbca1a1f3e632d1e95c9ce7e56 +Subproject commit ebc094f4c160f8aa3ebbe3f760fbf734b58bc5e6 diff --git a/MasterPassword/ObjC/iOS/MPLogsViewController.m b/MasterPassword/ObjC/iOS/MPLogsViewController.m index a0504405..0d7cc9a8 100644 --- a/MasterPassword/ObjC/iOS/MPLogsViewController.m +++ b/MasterPassword/ObjC/iOS/MPLogsViewController.m @@ -63,9 +63,9 @@ return; switchCloudStoreProgress = [PearlAlert showActivityWithTitle:@"Enumerating Stores"]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{ [self switchCloudStore]; - }); + } ); } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil]; } } cancelTitle:[PearlStrings get].commonButtonCancel @@ -75,39 +75,88 @@ - (void)switchCloudStore { NSError *error = nil; + NSURL *cloudStoreDirectory = [[MPiOSAppDelegate get].storeManager URLForCloudStoreDirectory]; NSURL *cloudContentDirectory = [[MPiOSAppDelegate get].storeManager URLForCloudContentDirectory]; NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:cloudContentDirectory includingPropertiesForKeys:nil - options:NSDirectoryEnumerationSkipsHiddenFiles error:&error]; + options:NSDirectoryEnumerationSkipsHiddenFiles error:&error]; if (!contents) err(@"While enumerating cloud contents: %@", error); - NSMutableArray *contentNames = [NSMutableArray arrayWithCapacity:[contents count]]; - BOOL directory = NO; + BOOL directory; + NSMutableDictionary *stores = [NSMutableDictionary dictionaryWithCapacity:[contents count]]; + NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; + NSPersistentStoreCoordinator *storePSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; + NSFetchRequest *usersFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )]; + NSFetchRequest *sitesFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; for (NSURL *content in contents) - if ([[NSFileManager defaultManager] fileExistsAtPath:content.path isDirectory:&directory] && directory) - [contentNames addObject:[content lastPathComponent]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:content.path isDirectory:&directory] && directory) { + NSString *contentString = [content lastPathComponent]; + NSUInteger lastDash = [contentString rangeOfString:@"-" options:NSBackwardsSearch].location; + NSString *storeDescription = lastDash == NSNotFound? contentString: [contentString substringFromIndex:lastDash + 1]; + NSPersistentStore *store = nil; + @try { + NSURL *storeURL = [[cloudStoreDirectory + URLByAppendingPathComponent:[content lastPathComponent] isDirectory:NO] + URLByAppendingPathExtension:@"sqlite"]; + if (!(store = [storePSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil + URL:storeURL options:@{ + NSPersistentStoreUbiquitousContentNameKey : [[MPiOSAppDelegate get].storeManager valueForKey:@"contentName"], + NSPersistentStoreUbiquitousContentURLKey : content, + NSMigratePersistentStoresAutomaticallyOption : @YES, + NSInferMappingModelAutomaticallyOption : @YES, + NSPersistentStoreFileProtectionKey : NSFileProtectionComplete + } error:&error])) { + wrn(@"Couldn't describe store opening %@: %@", [content lastPathComponent], error); + continue; + } + + NSUInteger userCount, siteCount; + NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + moc.persistentStoreCoordinator = storePSC; + if ((userCount = [moc countForFetchRequest:usersFetchRequest error:&error]) == NSNotFound) { + wrn(@"Couldn't describe store userCount %@: %@", [content lastPathComponent], error); + continue; + } + if ((siteCount = [moc countForFetchRequest:sitesFetchRequest error:&error]) == NSNotFound) { + wrn(@"Couldn't describe store siteCount %@: %@", [content lastPathComponent], error); + continue; + } + + storeDescription = PearlString( @"%@: %dU, %dS", storeDescription, userCount, siteCount ); + } + @catch (NSException *exception) { + wrn(@"Couldn't describe store %@: exception %@", [content lastPathComponent], exception); + } + @finally { + if (store) if (![storePSC removePersistentStore:store error:&error]) + wrn(@"Couldn't remove store %@: %@", [content lastPathComponent], error); + [stores setObject:storeDescription forKey:[content lastPathComponent]]; + } + } NSString *storeUUID = [[MPiOSAppDelegate get].storeManager valueForKey:@"storeUUID_ThreadSafe"]; - dispatch_async(dispatch_get_main_queue(), ^{ + NSUInteger lastDash = [storeUUID rangeOfString:@"-" options:NSBackwardsSearch].location; + NSString *title = PearlString( @"Current: %@", lastDash == NSNotFound? storeUUID: [storeUUID substringFromIndex:lastDash + 1] ); + dispatch_async( dispatch_get_main_queue(), ^{ [switchCloudStoreProgress cancelAlertAnimated:YES]; - [PearlSheet showSheetWithTitle:storeUUID - viewStyle:UIActionSheetStyleAutomatic + NSArray *storeUUIDs = [stores allKeys]; + [PearlSheet showSheetWithTitle:title viewStyle:UIActionSheetStyleAutomatic initSheet:^(UIActionSheet *sheet) { - for (NSString *contentName in contentNames) { - [sheet addButtonWithTitle:contentName]; - } + for (NSString *contentName in storeUUIDs) + [sheet addButtonWithTitle:[stores objectForKey:contentName]]; } tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { if (buttonIndex == sheet.cancelButtonIndex) return; - - [[MPiOSAppDelegate get].storeManager setValue:[contentNames objectAtIndex:(unsigned)buttonIndex] forKey:@"storeUUID"]; + + [[MPiOSAppDelegate get].storeManager setValue:[storeUUIDs objectAtIndex:(unsigned)buttonIndex] + forKey:@"storeUUID"]; [[MPiOSAppDelegate get].storeManager reloadStore]; [[MPiOSAppDelegate get] signOutAnimated:YES]; } cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil otherTitles:nil]; - }); + } ); } - (IBAction)toggleLevelControl:(UISegmentedControl *)sender { diff --git a/MasterPassword/ObjC/iOS/MPSetupViewController.m b/MasterPassword/ObjC/iOS/MPSetupViewController.m index 7c0623d4..32c9d753 100644 --- a/MasterPassword/ObjC/iOS/MPSetupViewController.m +++ b/MasterPassword/ObjC/iOS/MPSetupViewController.m @@ -42,7 +42,7 @@ if (self.cloudSwitch) { [MPiOSConfig get].iCloudDecided = @YES; - [MPiOSAppDelegate get].storeManager.cloudEnabled = self.cloudSwitch.on; + [MPiOSConfig get].iCloudEnabled = @(self.cloudSwitch.on); } if (self.rememberLoginSwitch) [MPiOSConfig get].rememberLogin = @(self.rememberLoginSwitch.on); diff --git a/MasterPassword/ObjC/iOS/MPUnlockViewController.h b/MasterPassword/ObjC/iOS/MPUnlockViewController.h index e59967d3..6cfa7622 100644 --- a/MasterPassword/ObjC/iOS/MPUnlockViewController.h +++ b/MasterPassword/ObjC/iOS/MPUnlockViewController.h @@ -37,15 +37,14 @@ @property(weak, nonatomic) IBOutlet UIButton *emergencyPassword; @property(weak, nonatomic) IBOutlet UIView *emergencyContentTipContainer; -@property(nonatomic, strong) UIColor *avatarShadowColor; - - (IBAction)targetedUserAction:(UILongPressGestureRecognizer *)sender; -- (IBAction)facebook:(UIButton *)sender; -- (IBAction)twitter:(UIButton *)sender; -- (IBAction)google:(UIButton *)sender; -- (IBAction)mail:(UIButton *)sender; -- (IBAction)add:(UIButton *)sender; -- (IBAction)emergencyClose:(UIButton *)sender; -- (IBAction)emergencyCopy:(UIButton *)sender; +- (IBAction)facebook:(id)sender; +- (IBAction)twitter:(id)sender; +- (IBAction)google:(id)sender; +- (IBAction)mail:(id)sender; +- (IBAction)add:(id)sender; +- (IBAction)emergencyOpen:(id)sender; +- (IBAction)emergencyClose:(id)sender; +- (IBAction)emergencyCopy:(id)sender; @end diff --git a/MasterPassword/ObjC/iOS/MPUnlockViewController.m b/MasterPassword/ObjC/iOS/MPUnlockViewController.m index eef9d566..4c50c146 100644 --- a/MasterPassword/ObjC/iOS/MPUnlockViewController.m +++ b/MasterPassword/ObjC/iOS/MPUnlockViewController.m @@ -243,12 +243,17 @@ inf(@"Lock screen will disappear"); [self emergencyCloseAnimated:animated]; - [self.marqueeTipTimer invalidate]; [super viewWillDisappear:animated]; } +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + + if ([segue.identifier isEqualToString:@"MP_Settings"]) + [self.navigationController setNavigationBarHidden:NO animated:YES]; +} + - (BOOL)prefersStatusBarHidden { return YES; @@ -266,29 +271,8 @@ - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { - if (motion == UIEventSubtypeMotionShake) { - MPCheckpoint( MPCheckpointEmergencyGenerator, nil ); - [[self.view findFirstResponderInHierarchy] resignFirstResponder]; - - self.emergencyGeneratorContainer.alpha = 0; - self.emergencyGeneratorContainer.hidden = NO; - self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame, - self.emergencyGeneratorContainer.frame.origin.x - 100 ); - [UIView animateWithDuration:0.3 animations:^{ - self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame, - self.emergencyGeneratorContainer.frame.origin.x + 150 ); - self.emergencyGeneratorContainer.alpha = 1; - } completion:^(BOOL finished) { - if (!finished) - return; - - [self.emergencyName becomeFirstResponder]; - [UIView animateWithDuration:0.2 animations:^{ - self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame, - self.emergencyGeneratorContainer.frame.origin.x - 50 ); - }]; - }]; - } + if (motion == UIEventSubtypeMotionShake) + [self emergencyOpenAnimated:YES]; } - (void)marqueeTip { @@ -518,7 +502,6 @@ self.nameLabel.center = CGPointMake( 160, 94 ); self.nameLabel.backgroundColor = [UIColor blackColor]; self.oldNameLabel.center = self.nameLabel.center; - self.avatarShadowColor = [UIColor whiteColor]; } else if (!selectedUser && self.passwordView.alpha == 1) { // User was just deselected. @@ -529,7 +512,6 @@ self.nameLabel.center = CGPointMake( 160, 296 ); self.nameLabel.backgroundColor = [UIColor clearColor]; self.oldNameLabel.center = self.nameLabel.center; - self.avatarShadowColor = [UIColor lightGrayColor]; } // Lay out the word wall. @@ -913,12 +895,20 @@ }]; } -- (IBAction)emergencyClose:(UIButton *)sender { +- (IBAction)emergencyOpen:(id)sender { + + if ([sender isKindOfClass:[UIGestureRecognizer class]] && ((UIGestureRecognizer *)sender).state != UIGestureRecognizerStateBegan) + return; + + [self emergencyOpenAnimated:YES]; +} + +- (IBAction)emergencyClose:(id)sender { [self emergencyCloseAnimated:YES]; } -- (IBAction)emergencyCopy:(UIButton *)sender { +- (IBAction)emergencyCopy:(id)sender { inf(@"Copying emergency password for: %@", self.emergencyName.text); [UIPasteboard generalPasteboard].string = [self.emergencyPassword titleForState:UIControlStateNormal]; @@ -943,6 +933,38 @@ } ); } +- (void)emergencyOpenAnimated:(BOOL)animated { + + [[self.emergencyGeneratorContainer findFirstResponderInHierarchy] resignFirstResponder]; + + if (animated) { + self.emergencyGeneratorContainer.alpha = 0; + self.emergencyGeneratorContainer.hidden = NO; + self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame, + self.emergencyGeneratorContainer.frame.origin.x - 100 ); + + [UIView animateWithDuration:0.2 animations:^{ + self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame, + self.emergencyGeneratorContainer.frame.origin.x + 150 ); + self.emergencyGeneratorContainer.alpha = 1; + } completion:^(BOOL finished) { + if (finished) { + [self emergencyOpenAnimated:NO]; + [UIView animateWithDuration:0.2 animations:^{ + self.emergencyGeneratorContainer.frame = CGRectSetX( self.emergencyGeneratorContainer.frame, + self.emergencyGeneratorContainer.frame.origin.x - 50 ); + }]; + } + }]; + return; + } + + MPCheckpoint( MPCheckpointEmergencyGenerator, nil ); + self.emergencyGeneratorContainer.hidden = NO; + self.emergencyGeneratorContainer.alpha = 1; + [self.emergencyName becomeFirstResponder]; +} + - (void)emergencyCloseAnimated:(BOOL)animated { [[self.emergencyGeneratorContainer findFirstResponderInHierarchy] resignFirstResponder]; @@ -951,7 +973,8 @@ [UIView animateWithDuration:0.2 animations:^{ self.emergencyGeneratorContainer.alpha = 0; } completion:^(BOOL finished) { - [self emergencyCloseAnimated:NO]; + if (finished) + [self emergencyCloseAnimated:NO]; }]; return; } @@ -1043,7 +1066,7 @@ destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil]; } -- (IBAction)facebook:(UIButton *)sender { +- (IBAction)facebook:(id)sender { if (![SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) { [PearlAlert showAlertWithTitle:@"Facebook Not Enabled" message:@"To send tweets, configure Facebook from Settings." @@ -1058,7 +1081,7 @@ [self presentViewController:vc animated:YES completion:nil]; } -- (IBAction)twitter:(UIButton *)sender { +- (IBAction)twitter:(id)sender { if (![SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) { [PearlAlert showAlertWithTitle:@"Twitter Not Enabled" message:@"To send tweets, configure Twitter from Settings." @@ -1073,19 +1096,19 @@ [self presentViewController:vc animated:YES completion:nil]; } -- (IBAction)google:(UIButton *)sender { +- (IBAction)google:(id)sender { id shareDialog = [[GPPShare sharedInstance] shareDialog]; [[[shareDialog setURLToShare:[NSURL URLWithString:@"http://masterpasswordapp.com"]] setPrefillText:@"I've started doing passwords properly thanks to Master Password."] open]; } -- (IBAction)mail:(UIButton *)sender { +- (IBAction)mail:(id)sender { [[MPiOSAppDelegate get] showFeedbackWithLogs:NO forVC:self]; } -- (IBAction)add:(UIButton *)sender { +- (IBAction)add:(id)sender { [PearlSheet showSheetWithTitle:@"Follow Master Password" viewStyle:UIActionSheetStyleBlackTranslucent initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { diff --git a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m index 84e4386a..41605719 100644 --- a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m +++ b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m @@ -182,67 +182,12 @@ @try { [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:nil usingBlock: ^(NSNotification *note) { - if ([[MPiOSConfig get].sendInfo boolValue]) { - if ([PearlLogger get].printLevel > PearlLogLevelInfo) - [PearlLogger get].printLevel = PearlLogLevelInfo; - -#ifdef CRASHLYTICS - [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"]; - [[Crashlytics sharedInstance] setBoolValue:[self storeManager].cloudEnabled forKey:@"iCloud"]; - [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloudDecided boolValue] forKey:@"iCloudDecided"]; - [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendInfo boolValue] forKey:@"sendInfo"]; - [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"]; - [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"]; - [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"]; - [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"]; - [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"]; - [[Crashlytics sharedInstance] - setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"]; - [[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"]; -#endif - -#ifdef TESTFLIGHT_SDK_VERSION - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPConfig get].rememberLogin ) - forKey:@"rememberLogin"]; - [TestFlight addCustomEnvironmentInformation:PearlStringB( [self storeManager].cloudEnabled ) - forKey:@"iCloud"]; - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPConfig get].iCloudDecided ) - forKey:@"iCloudDecided"]; - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPiOSConfig get].sendInfo ) - forKey:@"sendInfo"]; - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPiOSConfig get].helpHidden ) - forKey:@"helpHidden"]; - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPiOSConfig get].showSetup ) - forKey:@"showQuickStart"]; - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [PearlConfig get].firstRun ) - forKey:@"firstRun"]; - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [PearlConfig get].launchCount ) - forKey:@"launchCount"]; - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [PearlConfig get].askForReviews ) - forKey:@"askForReviews"]; - [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [PearlConfig get].reviewAfterLaunches ) - forKey:@"reviewAfterLaunches"]; - [TestFlight addCustomEnvironmentInformation:[PearlConfig get].reviewedVersion - forKey:@"reviewedVersion"]; -#endif - MPCheckpoint( MPCheckpointConfig, @{ - @"rememberLogin" : @([[MPConfig get].rememberLogin boolValue]), - @"iCloud" : @([self storeManager].cloudEnabled), - @"iCloudDecided" : @([[MPConfig get].iCloudDecided boolValue]), - @"sendInfo" : @([[MPiOSConfig get].sendInfo boolValue]), - @"helpHidden" : @([[MPiOSConfig get].helpHidden boolValue]), - @"showQuickStart" : @([[MPiOSConfig get].showSetup boolValue]), - @"firstRun" : @([[PearlConfig get].firstRun boolValue]), - @"launchCount" : NilToNSNull([PearlConfig get].launchCount), - @"askForReviews" : @([[PearlConfig get].askForReviews boolValue]), - @"reviewAfterLaunches" : NilToNSNull([PearlConfig get].reviewAfterLaunches), - @"reviewedVersion" : NilToNSNull([PearlConfig get].reviewedVersion) - } ); - } + [self checkConfig]; }]; [[NSNotificationCenter defaultCenter] addObserverForName:kIASKAppSettingChanged object:nil queue:nil usingBlock:^(NSNotification *note) { - [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note userInfo:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification + object:note userInfo:nil]; }]; #ifdef ADHOC @@ -447,7 +392,8 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { inf(@"Re-activated"); - [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:application userInfo:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification + object:application userInfo:nil]; #ifdef LOCALYTICS [[LocalyticsSession sharedLocalyticsSession] resume]; @@ -635,17 +581,108 @@ otherTitles:[PearlStrings get].commonButtonContinue, nil]; } + #pragma mark - PearlConfigDelegate - (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)value { - if (configKey == @selector(traceMode)) { - [PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo; - inf(@"Trace is now: %@", [[MPiOSConfig get].traceMode boolValue]? @"ON": @"OFF"); + [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification + object:NSStringFromSelector( configKey ) userInfo:nil]; +} + +- (void)checkConfig { + + // iCloud enabled / disabled + if ([[MPiOSConfig get].iCloudEnabled boolValue] != self.storeManager.cloudEnabled) { + if ([[MPiOSConfig get].iCloudEnabled boolValue]) + [self.storeManager setCloudEnabledAndOverwriteCloudWithLocalIfConfirmed:^(void (^setConfirmationAnswer)(BOOL answer)) { + [PearlAlert showAlertWithTitle:@"Keep Sites?" + message:@"You can either revert to your old iCloud sites or overwrite them with your current sites." + viewStyle:UIAlertViewStyleDefault initAlert:nil + tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { + if (buttonIndex == [alert cancelButtonIndex]) + setConfirmationAnswer( NO ); + if (buttonIndex == [alert firstOtherButtonIndex]) + setConfirmationAnswer( YES ); + } + cancelTitle:@"Revert" otherTitles:@"Replace iCloud", nil]; + }]; + else + [self.storeManager setCloudDisabledAndOverwriteLocalWithCloudIfConfirmed:^(void (^setConfirmationAnswer)(BOOL answer)) { + [PearlAlert showAlertWithTitle:@"Keep iCloud Sites?" + message:@"You can either revert to your old sites or overwrite them with your current iCloud sites." + viewStyle:UIAlertViewStyleDefault initAlert:nil + tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { + if (buttonIndex == [alert cancelButtonIndex]) + setConfirmationAnswer( NO ); + if (buttonIndex == [alert firstOtherButtonIndex]) + setConfirmationAnswer( YES ); + } + cancelTitle:@"Revert" otherTitles:@"Copy iCloud", nil]; + }]; } - [[NSNotificationCenter defaultCenter] - postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey ) userInfo:nil]; + // Trace mode + [PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo; + + // Send info + if ([[MPiOSConfig get].sendInfo boolValue]) { + if ([PearlLogger get].printLevel > PearlLogLevelInfo) + [PearlLogger get].printLevel = PearlLogLevelInfo; + +#ifdef CRASHLYTICS + [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].rememberLogin boolValue] forKey:@"rememberLogin"]; + [[Crashlytics sharedInstance] setBoolValue:[self storeManager].cloudEnabled forKey:@"iCloud"]; + [[Crashlytics sharedInstance] setBoolValue:[[MPConfig get].iCloudDecided boolValue] forKey:@"iCloudDecided"]; + [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].sendInfo boolValue] forKey:@"sendInfo"]; + [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].helpHidden boolValue] forKey:@"helpHidden"]; + [[Crashlytics sharedInstance] setBoolValue:[[MPiOSConfig get].showSetup boolValue] forKey:@"showQuickStart"]; + [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].firstRun boolValue] forKey:@"firstRun"]; + [[Crashlytics sharedInstance] setIntValue:[[PearlConfig get].launchCount intValue] forKey:@"launchCount"]; + [[Crashlytics sharedInstance] setBoolValue:[[PearlConfig get].askForReviews boolValue] forKey:@"askForReviews"]; + [[Crashlytics sharedInstance] + setIntValue:[[PearlConfig get].reviewAfterLaunches intValue] forKey:@"reviewAfterLaunches"]; + [[Crashlytics sharedInstance] setObjectValue:[PearlConfig get].reviewedVersion forKey:@"reviewedVersion"]; +#endif + +#ifdef TESTFLIGHT_SDK_VERSION + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPConfig get].rememberLogin ) + forKey:@"rememberLogin"]; + [TestFlight addCustomEnvironmentInformation:PearlStringB( [self storeManager].cloudEnabled ) + forKey:@"iCloud"]; + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPConfig get].iCloudDecided ) + forKey:@"iCloudDecided"]; + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPiOSConfig get].sendInfo ) + forKey:@"sendInfo"]; + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPiOSConfig get].helpHidden ) + forKey:@"helpHidden"]; + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [MPiOSConfig get].showSetup ) + forKey:@"showQuickStart"]; + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [PearlConfig get].firstRun ) + forKey:@"firstRun"]; + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [PearlConfig get].launchCount ) + forKey:@"launchCount"]; + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [PearlConfig get].askForReviews ) + forKey:@"askForReviews"]; + [TestFlight addCustomEnvironmentInformation:PearlStringNSB( [PearlConfig get].reviewAfterLaunches ) + forKey:@"reviewAfterLaunches"]; + [TestFlight addCustomEnvironmentInformation:[PearlConfig get].reviewedVersion + forKey:@"reviewedVersion"]; +#endif + MPCheckpoint( MPCheckpointConfig, @{ + @"rememberLogin" : @([[MPConfig get].rememberLogin boolValue]), + @"iCloud" : @([self storeManager].cloudEnabled), + @"iCloudDecided" : @([[MPConfig get].iCloudDecided boolValue]), + @"sendInfo" : @([[MPiOSConfig get].sendInfo boolValue]), + @"helpHidden" : @([[MPiOSConfig get].helpHidden boolValue]), + @"showQuickStart" : @([[MPiOSConfig get].showSetup boolValue]), + @"firstRun" : @([[PearlConfig get].firstRun boolValue]), + @"launchCount" : NilToNSNull([PearlConfig get].launchCount), + @"askForReviews" : @([[PearlConfig get].askForReviews boolValue]), + @"reviewAfterLaunches" : NilToNSNull([PearlConfig get].reviewAfterLaunches), + @"reviewedVersion" : NilToNSNull([PearlConfig get].reviewedVersion) + } ); + } } @@ -665,6 +702,7 @@ - (void)ubiquityStoreManager:(UbiquityStoreManager *)manager didLoadStoreForCoordinator:(NSPersistentStoreCoordinator *)coordinator isCloud:(BOOL)isCloudStore { + [MPiOSConfig get].iCloudEnabled = @(isCloudStore); [super ubiquityStoreManager:manager didLoadStoreForCoordinator:coordinator isCloud:isCloudStore]; dispatch_async( dispatch_get_main_queue(), ^{ @@ -693,24 +731,27 @@ @"Waiting for your other device to auto‑correct the problem..." initAlert:^(UIAlertView *alert) { [alert addButtonWithTitle:@"Fix Now"]; + [alert addButtonWithTitle:@"Turn Off"]; }]; self.handleCloudContentAlert.tappedButtonBlock = ^(UIAlertView *alert, NSInteger buttonIndex) { - wSelf.fixCloudContentAlert = [PearlAlert showAlertWithTitle:@"Fix iCloud Now" message: - @"This problem can be auto‑corrected by opening the app on another device where you recently made changes.\n" - @"You can correct the problem from this device anyway, but recent changes made on another device might get lost.\n\n" - @"You can also turn iCloud off and go back to your local sites." - viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock: - ^(UIAlertView *alert_, NSInteger buttonIndex_) { - if (buttonIndex_ == alert_.cancelButtonIndex) - [wSelf showCloudContentAlert]; - if (buttonIndex_ == [alert_ firstOtherButtonIndex]) - [wSelf.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:YES]; - if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1) - wSelf.storeManager.cloudEnabled = NO; - } - cancelTitle:[PearlStrings get].commonButtonBack otherTitles:@"Fix Anyway", - @"Turn Off", nil]; + if (buttonIndex == [alert firstOtherButtonIndex]) + wSelf.fixCloudContentAlert = [PearlAlert showAlertWithTitle:@"Fix iCloud Now" message: + @"This problem can be auto‑corrected by opening the app on another device where you recently made changes.\n" + @"You can fix the problem from this device anyway, but recent changes from another device might get lost.\n\n" + @"You can also turn iCloud off for now." + viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock: + ^(UIAlertView *alert_, NSInteger buttonIndex_) { + if (buttonIndex_ == alert_.cancelButtonIndex) + [wSelf showCloudContentAlert]; + if (buttonIndex_ == [alert_ firstOtherButtonIndex]) + [wSelf.storeManager rebuildCloudContentFromCloudStoreOrLocalStore:YES]; + if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1) + [MPiOSConfig get].iCloudEnabled = NO; + } cancelTitle:[PearlStrings get].commonButtonBack + otherTitles:@"Fix Anyway", @"Turn Off", nil]; + if (buttonIndex == [alert firstOtherButtonIndex] + 1) + [MPiOSConfig get].iCloudEnabled = NO; }; } diff --git a/MasterPassword/ObjC/iOS/MPiOSConfig.h b/MasterPassword/ObjC/iOS/MPiOSConfig.h index 37522d43..cbb0b2b4 100644 --- a/MasterPassword/ObjC/iOS/MPiOSConfig.h +++ b/MasterPassword/ObjC/iOS/MPiOSConfig.h @@ -17,5 +17,6 @@ @property(nonatomic, retain) NSNumber *typeTipShown; @property(nonatomic, retain) NSNumber *loginNameTipShown; @property(nonatomic, retain) NSNumber *traceMode; +@property(nonatomic, retain) NSNumber *iCloudEnabled; @end diff --git a/MasterPassword/ObjC/iOS/MPiOSConfig.m b/MasterPassword/ObjC/iOS/MPiOSConfig.m index 8bf099fd..9fc01315 100644 --- a/MasterPassword/ObjC/iOS/MPiOSConfig.m +++ b/MasterPassword/ObjC/iOS/MPiOSConfig.m @@ -8,7 +8,7 @@ @implementation MPiOSConfig -@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode; +@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, iCloudEnabled; - (id)init { @@ -23,7 +23,8 @@ NSStringFromSelector( @selector(actionsTipShown) ) : @(!self.firstRun), NSStringFromSelector( @selector(typeTipShown) ) : @(!self.firstRun), NSStringFromSelector( @selector(loginNameTipShown) ) : @NO, - NSStringFromSelector( @selector(traceMode) ) : @NO + NSStringFromSelector( @selector(traceMode) ) : @NO, + NSStringFromSelector( @selector(iCloudEnabled) ) : @YES }]; return self; diff --git a/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard index 3626c9c9..013fe907 100644 --- a/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard @@ -1499,6 +1499,7 @@ You can use the words in the background for inspiration in finding a memorable m +