From b2624c7572b1e90060cd3c7db61c1ec3f18375be Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Sat, 12 Apr 2014 14:43:41 -0400 Subject: [PATCH] Emergency generator, avatar change, improvements. [ADDED] Ability to change avatar while creating new user. [FIXED] Transition oddness. [IMPROVED] Remove passwordsVC while not needed. [ADDED] Emergency generator. --- External/LoveLyndir | 2 +- External/Pearl | 2 +- External/UbiquityStoreManager | 2 +- MasterPassword/ObjC/MPAppDelegate_Store.m | 19 +- MasterPassword/ObjC/iOS/MPAvatarCell.m | 4 +- .../ObjC/iOS/MPCombinedViewController.h | 2 - .../ObjC/iOS/MPCombinedViewController.m | 88 +++-- MasterPassword/ObjC/iOS/MPEmergencySegue.h | 24 ++ MasterPassword/ObjC/iOS/MPEmergencySegue.m | 57 +++ .../ObjC/iOS/MPEmergencyViewController.h | 37 ++ .../ObjC/iOS/MPEmergencyViewController.m | 75 ++++ MasterPassword/ObjC/iOS/MPPasswordsSegue.h | 25 ++ MasterPassword/ObjC/iOS/MPPasswordsSegue.m | 63 +++ .../ObjC/iOS/MPPasswordsViewController.h | 4 +- .../ObjC/iOS/MPPasswordsViewController.m | 85 +++- .../ObjC/iOS/MPUnlockViewController.m | 3 +- .../ObjC/iOS/MPUsersViewController.h | 7 +- .../ObjC/iOS/MPUsersViewController.m | 118 +++--- MasterPassword/ObjC/iOS/MPiOSAppDelegate.m | 33 +- .../ObjC/iOS/MainStoryboard_iPhone.storyboard | 2 +- .../project.pbxproj | 34 ++ .../MasterPassword iOS (Development).xcscheme | 6 + MasterPassword/ObjC/iOS/Storyboard.storyboard | 373 +++++++++++++++++- 23 files changed, 910 insertions(+), 155 deletions(-) create mode 100644 MasterPassword/ObjC/iOS/MPEmergencySegue.h create mode 100644 MasterPassword/ObjC/iOS/MPEmergencySegue.m create mode 100644 MasterPassword/ObjC/iOS/MPEmergencyViewController.h create mode 100644 MasterPassword/ObjC/iOS/MPEmergencyViewController.m create mode 100644 MasterPassword/ObjC/iOS/MPPasswordsSegue.h create mode 100644 MasterPassword/ObjC/iOS/MPPasswordsSegue.m diff --git a/External/LoveLyndir b/External/LoveLyndir index 0b858644..ceed9e20 160000 --- a/External/LoveLyndir +++ b/External/LoveLyndir @@ -1 +1 @@ -Subproject commit 0b858644cb58c357e055b41c2747ad9d45fa9ce3 +Subproject commit ceed9e20009f2cf3679445e2c60b0f206aaef383 diff --git a/External/Pearl b/External/Pearl index d462ada8..8d3d345c 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit d462ada876939cab875e08736b1efc7af065848d +Subproject commit 8d3d345c4cc4c2630167d173767a856988c9d460 diff --git a/External/UbiquityStoreManager b/External/UbiquityStoreManager index ef9aa1c2..c02affe8 160000 --- a/External/UbiquityStoreManager +++ b/External/UbiquityStoreManager @@ -1 +1 @@ -Subproject commit ef9aa1c29ed6e1729be4d6a3dd65e2c8a289bf4c +Subproject commit c02affe877a6de49bbe4055b8dceed52613f4e12 diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.m b/MasterPassword/ObjC/MPAppDelegate_Store.m index 6caac068..de775a54 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.m +++ b/MasterPassword/ObjC/MPAppDelegate_Store.m @@ -408,11 +408,9 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext, } MPElementType type = activeUser.defaultType; - NSString *typeEntityClassName = [MPAlgorithmDefault classNameOfType:type]; - - MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityClassName - inManagedObjectContext:context]; + NSString *typeEntityName = [MPAlgorithmDefault classNameOfType:type]; + MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context]; element.name = siteName; element.user = activeUser; element.type = type; @@ -440,9 +438,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext, else { // Type requires a different class of element. Recreate the element. - MPElementEntity *newElement - = [NSEntityDescription insertNewObjectForEntityForName:[element.algorithm classNameOfType:type] - inManagedObjectContext:context]; + NSString *typeEntityName = [element.algorithm classNameOfType:type]; + MPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context]; newElement.type = type; newElement.name = element.name; newElement.user = element.user; @@ -648,8 +645,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext, // Make sure there is a user. if (!user) { - user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] ) - inManagedObjectContext:context]; + user = [MPUserEntity insertNewObjectInContext:context]; user.name = importUserName; user.keyID = importKeyID; dbg(@"Created User: %@", [user debugDescription]); @@ -665,9 +661,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext, NSString *exportContent = siteElements[5]; // Create new site. - MPElementEntity - *element = [NSEntityDescription insertNewObjectForEntityForName:[MPAlgorithmForVersion( version ) classNameOfType:type] - inManagedObjectContext:context]; + NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type]; + MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context]; element.name = name; element.user = user; element.type = type; diff --git a/MasterPassword/ObjC/iOS/MPAvatarCell.m b/MasterPassword/ObjC/iOS/MPAvatarCell.m index 063b4633..37118fad 100644 --- a/MasterPassword/ObjC/iOS/MPAvatarCell.m +++ b/MasterPassword/ObjC/iOS/MPAvatarCell.m @@ -103,9 +103,9 @@ const long MPAvatarAdd = 10000; - (void)setAvatar:(long)avatar { - _avatar = avatar; + _avatar = avatar == MPAvatarAdd? MPAvatarAdd: (avatar + MPAvatarCount) % MPAvatarCount; - if (avatar == MPAvatarAdd) { + if (_avatar == MPAvatarAdd) { self.avatarImageView.image = [UIImage imageNamed:@"avatar-add"]; self.name = strl( @"New User" ); _newUser = YES; diff --git a/MasterPassword/ObjC/iOS/MPCombinedViewController.h b/MasterPassword/ObjC/iOS/MPCombinedViewController.h index cec775b7..814892f2 100644 --- a/MasterPassword/ObjC/iOS/MPCombinedViewController.h +++ b/MasterPassword/ObjC/iOS/MPCombinedViewController.h @@ -28,6 +28,4 @@ typedef NS_ENUM(NSUInteger, MPCombinedMode) { @property(assign, nonatomic) MPCombinedMode mode; -- (IBAction)doSignOut:(UIBarButtonItem *)sender; - @end diff --git a/MasterPassword/ObjC/iOS/MPCombinedViewController.m b/MasterPassword/ObjC/iOS/MPCombinedViewController.m index 2757659b..ee7fdf0f 100644 --- a/MasterPassword/ObjC/iOS/MPCombinedViewController.m +++ b/MasterPassword/ObjC/iOS/MPCombinedViewController.m @@ -17,21 +17,22 @@ // #import "MPCombinedViewController.h" -#import "MPiOSAppDelegate.h" -#import "MPAppDelegate_Store.h" -#import "MPAppDelegate_Key.h" #import "MPUsersViewController.h" #import "MPPasswordsViewController.h" +#import "MPEmergencySegue.h" +#import "MPEmergencyViewController.h" +#import "MPPasswordsSegue.h" @interface MPCombinedViewController() @property(strong, nonatomic) IBOutlet NSLayoutConstraint *passwordsTopConstraint; -@property(nonatomic, strong) MPUsersViewController *usersVC; -@property(nonatomic, strong) MPPasswordsViewController *passwordsVC; +@property(nonatomic, weak) MPUsersViewController *usersVC; +@property(nonatomic, weak) MPEmergencyViewController *emergencyVC; @end @implementation MPCombinedViewController { NSArray *_notificationObservers; + MPPasswordsViewController *_passwordsVC; } - (void)viewDidLoad { @@ -66,8 +67,16 @@ if ([segue.identifier isEqualToString:@"users"]) self.usersVC = segue.destinationViewController; - if ([segue.identifier isEqualToString:@"passwords"]) - self.passwordsVC = segue.destinationViewController; + if ([segue.identifier isEqualToString:@"passwords"]) { + NSAssert([segue isKindOfClass:[MPPasswordsSegue class]], @"passwords segue should be MPPasswordsSegue: %@", segue); + NSAssert([sender isKindOfClass:[NSDictionary class]], @"sender should be dictionary: %@", sender); + NSAssert([[sender objectForKey:@"animated"] isKindOfClass:[NSNumber class]], @"sender should contain 'animated': %@", sender); + [(MPPasswordsSegue *)segue setAnimated:[sender[@"animated"] boolValue]]; + UIViewController *destinationVC = segue.destinationViewController; + _passwordsVC = [destinationVC isKindOfClass:[MPPasswordsViewController class]]? (MPPasswordsViewController *)destinationVC: nil; + } + if ([segue.identifier isEqualToString:@"emergency"]) + self.emergencyVC = segue.destinationViewController; } - (UIStatusBarStyle)preferredStatusBarStyle { @@ -75,6 +84,29 @@ return UIStatusBarStyleLightContent; } +- (BOOL)canBecomeFirstResponder { + + return YES; +} + +- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { + + if (motion == UIEventSubtypeMotionShake && !self.emergencyVC) + [self performSegueWithIdentifier:@"emergency" sender:self]; +} + +- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController + fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier { + + if ([identifier isEqualToString:@"emergency"]) { + MPEmergencySegue *segue = [[MPEmergencySegue alloc] initWithIdentifier:identifier + source:fromViewController destination:toViewController]; + segue.unwind = YES; + dbg_return(segue); + } + + dbg_return((id)nil); +} #pragma mark - Properties @@ -85,30 +117,30 @@ - (void)setMode:(MPCombinedMode)mode animated:(BOOL)animated { + if (_mode == mode && animated) + return; _mode = mode; [self becomeFirstResponder]; - switch (self.mode) { - case MPCombinedModeUserSelection: { - [self.usersVC setActive:YES animated:animated]; - [self.passwordsVC setActive:NO animated:animated]; -// MPUsersViewController *usersVC = [self.storyboard instantiateViewControllerWithIdentifier:@"MPUsersViewController"]; -// [self setViewControllers:@[ usersVC ] direction:UIPageViewControllerNavigationDirectionReverse -// animated:animated completion:nil]; - break; - } - case MPCombinedModePasswordSelection: { - [self.usersVC setActive:NO animated:animated]; - [self.passwordsVC setActive:YES animated:animated]; -// MPPasswordsViewController *passwordsVC = [self.storyboard instantiateViewControllerWithIdentifier:@"MPPasswordsViewController"]; -// [self setViewControllers:@[ passwordsVC ] direction:UIPageViewControllerNavigationDirectionForward -// animated:animated completion:nil]; - break; + switch (self.mode) { + case MPCombinedModeUserSelection: { + [self.usersVC setActive:YES animated:animated]; + if (_passwordsVC) { + MPPasswordsSegue *segue = [[MPPasswordsSegue alloc] initWithIdentifier:@"passwords" source:_passwordsVC destination:self]; + [self prepareForSegue:segue sender:@{ @"animated" : @(animated) }]; + [segue perform]; } + break; } + case MPCombinedModePasswordSelection: { + [self.usersVC setActive:NO animated:animated]; + [self performSegueWithIdentifier:@"passwords" sender:@{ @"animated" : @(animated) }]; + break; + } + } - [self.passwordsTopConstraint apply]; + [self.passwordsTopConstraint apply]; } #pragma mark - Private @@ -144,12 +176,4 @@ _notificationObservers = nil; } - -#pragma mark - Actions - -- (IBAction)doSignOut:(UIBarButtonItem *)sender { - - [[MPiOSAppDelegate get] signOutAnimated:YES]; -} - @end diff --git a/MasterPassword/ObjC/iOS/MPEmergencySegue.h b/MasterPassword/ObjC/iOS/MPEmergencySegue.h new file mode 100644 index 00000000..b527d467 --- /dev/null +++ b/MasterPassword/ObjC/iOS/MPEmergencySegue.h @@ -0,0 +1,24 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPEmergencySegue.h +// MPEmergencySegue +// +// Created by lhunath on 2014-04-09. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. +// + +#import + +@interface MPEmergencySegue : UIStoryboardSegue + +@property(nonatomic) BOOL unwind; +@end diff --git a/MasterPassword/ObjC/iOS/MPEmergencySegue.m b/MasterPassword/ObjC/iOS/MPEmergencySegue.m new file mode 100644 index 00000000..1bef02c3 --- /dev/null +++ b/MasterPassword/ObjC/iOS/MPEmergencySegue.m @@ -0,0 +1,57 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPEmergencySegue.h +// MPEmergencySegue +// +// Created by lhunath on 2014-04-09. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. +// + +#import "MPEmergencySegue.h" + +@implementation MPEmergencySegue { +} + +- (void)perform { + + UIViewController *sourceViewController = self.sourceViewController; + UIViewController *destinationViewController = self.destinationViewController; + + if (!self.unwind) { + // Winding + [sourceViewController addChildViewController:destinationViewController]; + [sourceViewController.view addSubview:destinationViewController.view]; + CGRectSetY(destinationViewController.view.bounds, sourceViewController.view.frame.size.height); + [UIView transitionWithView:sourceViewController.view duration:0.3f options:UIViewAnimationOptionAllowAnimatedContent + animations:^{ + CGRectSetY(destinationViewController.view.bounds, 0); + } completion:^(BOOL finished) { + if (finished) + [destinationViewController didMoveToParentViewController:sourceViewController]; + }]; + } + else { + // Unwinding + [sourceViewController willMoveToParentViewController:nil]; + [UIView transitionWithView:sourceViewController.parentViewController.view duration:0.3f options:UIViewAnimationOptionAllowAnimatedContent + animations:^{ + CGRectSetY(sourceViewController.view.bounds, sourceViewController.parentViewController.view.frame.size.height); + } completion:^(BOOL finished) { + if (finished) { + [sourceViewController.view removeFromSuperview]; + [sourceViewController removeFromParentViewController]; + } + }]; + } +} + +@end diff --git a/MasterPassword/ObjC/iOS/MPEmergencyViewController.h b/MasterPassword/ObjC/iOS/MPEmergencyViewController.h new file mode 100644 index 00000000..34355fcd --- /dev/null +++ b/MasterPassword/ObjC/iOS/MPEmergencyViewController.h @@ -0,0 +1,37 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPCombinedViewController.h +// MPCombinedViewController +// +// Created by lhunath on 2014-03-08. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. +// + +#import "LLGitTip.h" + +@interface MPEmergencyViewController : UIViewController + +@property(weak, nonatomic) IBOutlet UIView *emergencyGeneratorDialog; +@property(weak, nonatomic) IBOutlet UIView *emergencyGeneratorContainer; +@property(weak, nonatomic) IBOutlet UITextField *emergencyName; +@property(weak, nonatomic) IBOutlet UITextField *emergencyMasterPassword; +@property(weak, nonatomic) IBOutlet UITextField *emergencySite; +@property(weak, nonatomic) IBOutlet UIStepper *emergencyCounterStepper; +@property(weak, nonatomic) IBOutlet UISegmentedControl *emergencyTypeControl; +@property(weak, nonatomic) IBOutlet UILabel *emergencyCounter; +@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *emergencyActivity; +@property(weak, nonatomic) IBOutlet UIButton *emergencyPassword; +@property(weak, nonatomic) IBOutlet UIView *emergencyContentTipContainer; + +- (IBAction)emergencyCopy:(id)sender; + +@end diff --git a/MasterPassword/ObjC/iOS/MPEmergencyViewController.m b/MasterPassword/ObjC/iOS/MPEmergencyViewController.m new file mode 100644 index 00000000..234e6a32 --- /dev/null +++ b/MasterPassword/ObjC/iOS/MPEmergencyViewController.m @@ -0,0 +1,75 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPCombinedViewController.h +// MPCombinedViewController +// +// Created by lhunath on 2014-03-08. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. +// + +#import "MPEmergencyViewController.h" +#import "MPEntities.h" +#import "MPAvatarCell.h" +#import "MPiOSAppDelegate.h" +#import "MPAppDelegate_Store.h" +#import "MPAppDelegate_Key.h" +#import "MPEmergencySegue.h" + +@implementation MPEmergencyViewController + +- (void)viewDidLoad { + + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor clearColor]; + self.emergencyGeneratorDialog.layer.cornerRadius = 5; +} + +- (BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender { + + return [self respondsToSelector:action]; +} + +#pragma mark - Actions + +- (IBAction)unwindToCombined:(UIStoryboardSegue *)sender { + + dbg(@"unwindToCombined:%@", sender); +} + +#pragma mark - UITextFieldDelegate + +- (void)textFieldDidEndEditing:(UITextField *)textField { +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + + [textField resignFirstResponder]; + return YES; +} + +// This isn't really in UITextFieldDelegate. We fake it from UITextFieldTextDidChangeNotification. +- (void)textFieldEditingChanged:(UITextField *)textField { + +} + +#pragma mark - Actions + +- (IBAction)emergencyClose:(id)sender { + + [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; +} + +- (IBAction)emergencyCopy:(id)sender { +} + +@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordsSegue.h b/MasterPassword/ObjC/iOS/MPPasswordsSegue.h new file mode 100644 index 00000000..29dabbbe --- /dev/null +++ b/MasterPassword/ObjC/iOS/MPPasswordsSegue.h @@ -0,0 +1,25 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPPasswordsSegue.h +// MPPasswordsSegue +// +// Created by lhunath on 2014-04-12. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. +// + +#import + +@interface MPPasswordsSegue : UIStoryboardSegue + +@property (nonatomic, assign) BOOL animated; + +@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordsSegue.m b/MasterPassword/ObjC/iOS/MPPasswordsSegue.m new file mode 100644 index 00000000..7e2f4b1f --- /dev/null +++ b/MasterPassword/ObjC/iOS/MPPasswordsSegue.m @@ -0,0 +1,63 @@ +/** + * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com) + * + * See the enclosed file LICENSE for license information (LGPLv3). If you did + * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt + * + * @author Maarten Billemont + * @license http://www.gnu.org/licenses/lgpl-3.0.txt + */ + +// +// MPPasswordsSegue.h +// MPPasswordsSegue +// +// Created by lhunath on 2014-04-12. +// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. +// + +#import "MPPasswordsSegue.h" +#import "MPPasswordsViewController.h" + +@implementation MPPasswordsSegue { +} + +- (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination { + + if (!(self = [super initWithIdentifier:identifier source:source destination:destination])) + return nil; + + self.animated = YES; + + return self; +} + +- (void)perform { + + if ([self.destinationViewController isKindOfClass:[MPPasswordsViewController class]]) { + __weak MPPasswordsViewController *passwordsVC = self.destinationViewController; + + [self.sourceViewController addChildViewController:passwordsVC]; + [[passwordsVC.parentViewController view] addSubview:passwordsVC.view]; + passwordsVC.active = NO; + [passwordsVC setActive:YES animated:self.animated completion:^(BOOL finished) { + if (!finished) + return; + + [passwordsVC didMoveToParentViewController:passwordsVC.parentViewController]; + }]; + } else if ([self.sourceViewController isKindOfClass:[MPPasswordsViewController class]]) { + __weak MPPasswordsViewController *passwordsVC = self.sourceViewController; + + [passwordsVC willMoveToParentViewController:nil]; + [passwordsVC setActive:NO animated:self.animated completion:^(BOOL finished) { + if (!finished) + return; + + [passwordsVC.view removeFromSuperview]; + [passwordsVC removeFromParentViewController]; + }]; + } +} + +@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.h b/MasterPassword/ObjC/iOS/MPPasswordsViewController.h index c439a9d2..e183b6c2 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.h +++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.h @@ -31,6 +31,8 @@ @property(assign, nonatomic) BOOL active; @property(nonatomic, copy) NSString *originalQuery; -- (void)setActive:(BOOL)active animated:(BOOL)animated; +- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void (^)(BOOL finished))completion; + +- (IBAction)action:(id)sender; @end diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m index 83c1b6ce..d1b6973a 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m +++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m @@ -20,7 +20,6 @@ #import "MPiOSAppDelegate.h" #import "MPAppDelegate_Store.h" #import "MPPasswordLargeCell.h" -#import "UIScrollView+PearlAdjustInsets.h" #import "MPPasswordTypesCell.h" #import "MPPasswordSmallCell.h" #import "UIColor+Expanded.h" @@ -62,6 +61,7 @@ [self registerObservers]; [self observeStore]; + [self updatePasswords]; } - (void)viewWillDisappear:(BOOL)animated { @@ -94,7 +94,7 @@ - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { if (collectionView == self.passwordCollectionView) - dbg_return_tr([self.fetchedResultsController.sections count], @); + return [self.fetchedResultsController.sections count]; Throw(@"Unexpected collection view: %@", collectionView); } @@ -102,8 +102,9 @@ - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { if (collectionView == self.passwordCollectionView) - dbg_return_tr(!self.query.length? 0: ((id)self.fetchedResultsController.sections[section]).numberOfObjects + - (_exactMatch? 0: 1), @, @(section)); + return ![MPiOSAppDelegate get].activeUserOID? 0: + ((id)self.fetchedResultsController.sections[section]).numberOfObjects + + (!_exactMatch && [[self query] length]? 1: 0); Throw(@"Unexpected collection view: %@", collectionView); } @@ -121,11 +122,11 @@ cell = [MPPasswordSmallCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath]; } else - // New Site. + // New Site. cell = [MPPasswordTypesCell dequeueCellForTransientSite:self.query fromCollectionView:collectionView atIndexPath:indexPath]; [UIView setAnimationsEnabled:YES]; - dbg_return(cell, indexPath); + return cell; } Throw(@"Unexpected collection view: %@", collectionView); @@ -220,9 +221,11 @@ if (!updatesForType) _fetchedUpdates[@(type)] = updatesForType = [NSMutableArray new]; - [updatesForType addObject:@{ @"object" : NilToNSNull(anObject), - @"indexPath" : NilToNSNull(indexPath), - @"newIndexPath" : NilToNSNull(newIndexPath) }]; + [updatesForType addObject:@{ + @"object" : NilToNSNull(anObject), + @"indexPath" : NilToNSNull(indexPath), + @"newIndexPath" : NilToNSNull(newIndexPath) + }]; switch (type) { case NSFetchedResultsChangeInsert: dbg(@"didChangeObject: insert: %@", [updatesForType lastObject]); @@ -248,8 +251,10 @@ if (!updatesForType) _fetchedUpdates[@(type << 3)] = updatesForType = [NSMutableArray new]; - [updatesForType addObject:@{ @"sectionInfo" : NilToNSNull(sectionInfo), - @"index" : @(sectionIndex) }]; + [updatesForType addObject:@{ + @"sectionInfo" : NilToNSNull(sectionInfo), + @"index" : @(sectionIndex) + }]; switch (type) { case NSFetchedResultsChangeInsert: dbg(@"didChangeSection: insert: %@", [updatesForType lastObject]); @@ -458,7 +463,7 @@ NSString *query = self.query; NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID; - if (!activeUserOID || ![query length]) { + if (!activeUserOID) { self.passwordsSearchBar.text = nil; PearlMainQueue( ^{ [self.passwordCollectionView reloadData]; } ); return; @@ -466,8 +471,10 @@ [self.fetchedResultsController.managedObjectContext performBlock:^{ NSError *error = nil; - self.fetchedResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat: - @"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]; + self.fetchedResultsController.fetchRequest.predicate = + [query length]? + [NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]: + [NSPredicate predicateWithFormat:@"user == %@", activeUserOID]; if (![self.fetchedResultsController performFetch:&error]) err(@"Couldn't fetch elements: %@", error); @@ -536,14 +543,14 @@ - (void)setActive:(BOOL)active { - [self setActive:active animated:YES]; + [self setActive:active animated:NO completion:nil]; } -- (void)setActive:(BOOL)active animated:(BOOL)animated { +- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void (^)(BOOL finished))completion { _active = active; - [UIView animateWithDuration:animated? 0.3f: 0 animations:^{ + [UIView animateWithDuration:animated? 0.4f: 0 animations:^{ self.navigationBarToPasswordsConstraint.priority = active? UILayoutPriorityDefaultHigh: 1; self.navigationBarToTopConstraint.priority = active? 1: UILayoutPriorityDefaultHigh; self.passwordsToBottomConstraint.priority = active? 1: UILayoutPriorityDefaultHigh; @@ -551,9 +558,51 @@ [self.navigationBarToPasswordsConstraint apply]; [self.navigationBarToTopConstraint apply]; [self.passwordsToBottomConstraint apply]; - }]; + } completion:completion]; } #pragma mark - Actions +- (IBAction)action:(id)sender { + + [PearlSheet showSheetWithTitle:nil viewStyle:UIActionSheetStyleAutomatic + initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { + if (buttonIndex == [sheet cancelButtonIndex]) + return; + + switch (buttonIndex - [sheet firstOtherButtonIndex]) { + case 0: { + inf(@"Action: Guide"); + [[MPiOSAppDelegate get] showGuide]; + break; + } + case 1: { + inf(@"Action: Preferences"); + [self performSegueWithIdentifier:@"MP_UserProfile" sender:self]; + break; + } + case 2: { + inf(@"Action: Other Apps"); + [self performSegueWithIdentifier:@"MP_OtherApps" sender:self]; + break; + } + case 3: { + inf(@"Action: Feedback via Mail"); + [[MPiOSAppDelegate get] showFeedbackWithLogs:YES forVC:self]; + break; + } + default: { + wrn(@"Unsupported action: %ld", (long)(buttonIndex - [sheet firstOtherButtonIndex])); + break; + } + } + } + cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil otherTitles: + @"Overview", + @"User Profile", + @"Other Apps", + @"Feedback", + nil]; +} + @end diff --git a/MasterPassword/ObjC/iOS/MPUnlockViewController.m b/MasterPassword/ObjC/iOS/MPUnlockViewController.m index 281319f2..b5183caa 100644 --- a/MasterPassword/ObjC/iOS/MPUnlockViewController.m +++ b/MasterPassword/ObjC/iOS/MPUnlockViewController.m @@ -416,8 +416,7 @@ - (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar { if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] ) - inManagedObjectContext:context]; + MPUserEntity *newUser = [MPUserEntity insertNewObjectInContext:context]; [self showNewUserNameAlertFor:newUser saveInContext:context completion:^(BOOL finished) { newUserAvatar.selected = NO; diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.h b/MasterPassword/ObjC/iOS/MPUsersViewController.h index a0b1e5a5..42c0644b 100644 --- a/MasterPassword/ObjC/iOS/MPUsersViewController.h +++ b/MasterPassword/ObjC/iOS/MPUsersViewController.h @@ -27,14 +27,19 @@ @property(weak, nonatomic) IBOutlet LLGitTip *gitTipButton; @property(weak, nonatomic) IBOutlet UITextField *entryField; @property(weak, nonatomic) IBOutlet UILabel *entryLabel; +@property(weak, nonatomic) IBOutlet UILabel *entryTip; +@property(weak, nonatomic) IBOutlet UIView *entryTipContainer; @property(weak, nonatomic) IBOutlet UIView *entryContainer; @property(weak, nonatomic) IBOutlet UIView *footerContainer; +@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *storeLoadingActivity; @property(weak, nonatomic) IBOutlet UICollectionView *avatarCollectionView; -@property(weak, nonatomic) IBOutlet NSLayoutConstraint *avatarCollectionCenterConstraint; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *navigationBarToTopConstraint; +@property (strong, nonatomic) IBOutlet UIButton *nextAvatarButton; +@property (strong, nonatomic) IBOutlet UIButton *previousAvatarButton; @property(assign, nonatomic) BOOL active; - (void)setActive:(BOOL)active animated:(BOOL)animated; +- (IBAction)changeAvatar:(UIButton *)sender; @end diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.m b/MasterPassword/ObjC/iOS/MPUsersViewController.m index bc160709..e313105a 100644 --- a/MasterPassword/ObjC/iOS/MPUsersViewController.m +++ b/MasterPassword/ObjC/iOS/MPUsersViewController.m @@ -98,17 +98,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { [self.marqueeTipTimer invalidate]; } -- (BOOL)canBecomeFirstResponder { - - return YES; -} - -- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { - -// if (motion == UIEventSubtypeMotionShake) -// [self emergencyOpenAnimated:YES]; -} - #pragma mark - UITextFieldDelegate - (void)textFieldDidEndEditing:(UITextField *)textField { @@ -138,7 +127,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { if (!signedIn) { // Sign in failed. - // TODO: warn user + [self showEntryTip:strl( @"Incorrect password! Typo?" )]; return; } }]; @@ -149,7 +138,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { NSString *userName = self.entryField.text; if (![userName length]) { // No name entered. - // TODO: warn user + [self showEntryTip:strl( @"First, enter your name" )]; return NO; } @@ -161,7 +150,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { NSString *masterPassword = self.entryField.text; if (![masterPassword length]) { // No password entered. - // TODO: warn user + [self showEntryTip:strl( @"Pick a master password" )]; return NO; } @@ -172,43 +161,43 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { NSString *masterPassword = self.entryField.text; if (![masterPassword length]) { // No password entered. - // TODO: warn user + [self showEntryTip:strl( @"Confirm your master password" )]; return NO; } if (![masterPassword isEqualToString:_masterPasswordChoice]) { // Master password confirmation failed. - // TODO: warn user + [self showEntryTip:strl( @"Looks like a typo! Try again." )]; self.activeUserState = MPActiveUserStateMasterPasswordChoice; return NO; } [self.entryField endEditing:YES]; - [self selectedAvatar].spinnerActive = YES; - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + MPAvatarCell *avatarCell = [self selectedAvatar]; + avatarCell.spinnerActive = YES; + if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { BOOL isNew = NO; - MPUserEntity *user = [self selectedUserInContext:context isNew:&isNew]; + MPUserEntity *user = [self userForAvatar:avatarCell inContext:context isNew:&isNew]; if (isNew) { - user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] ) - inManagedObjectContext:context]; - MPAvatarCell *avatarCell = [self selectedAvatar]; + user = [MPUserEntity insertNewObjectInContext:context]; user.avatar = avatarCell.avatar; user.name = avatarCell.name; } BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context usingMasterPassword:masterPassword]; - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + PearlMainQueue( ^{ self.entryField.text = @""; [self selectedAvatar].spinnerActive = NO; if (!signedIn) { // Sign in failed, shouldn't happen for a new user. - // TODO: warn user + [self showEntryTip:strl( @"Couldn't create new user." )]; self.activeUserState = MPActiveUserStateNone; return; } - }]; - }]; + } ); + }]) + avatarCell.spinnerActive = NO; break; } @@ -274,8 +263,8 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { if (collectionView == self.avatarCollectionView) { MPAvatarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPAvatarCell reuseIdentifier] forIndexPath:indexPath]; [cell addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPress:)]]; - [self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO]; [self updateModeForAvatar:cell atIndexPath:indexPath animated:NO]; + [self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO]; BOOL isNew = NO; MPUserEntity *user = [self userForIndexPath:indexPath inContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady] @@ -399,6 +388,21 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { #pragma mark - Private +- (void)showEntryTip:(NSString *)message { + + self.entryTip.text = message; + [UIView animateWithDuration:0.3f animations:^{ + self.entryTipContainer.alpha = 1; + } completion:^(BOOL finished) { + if (finished) + PearlMainQueueAfter( 4, ^{ + [UIView animateWithDuration:0.3f animations:^{ + self.entryTipContainer.alpha = 0; + }]; + } ); + }]; +} + - (void)firedMarqueeTimer:(NSTimer *)timer { [UIView animateWithDuration:0.5 animations:^{ @@ -464,17 +468,8 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { - (void)updateAvatarAtIndexPath:(NSIndexPath *)indexPath { MPAvatarCell *cell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath]; - [self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO]; [self updateModeForAvatar:cell atIndexPath:indexPath animated:NO]; -} - -- (void)updateVisibilityForAvatar:(MPAvatarCell *)cell atIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated { - - CGFloat current = [self.avatarCollectionView layoutAttributesForItemAtIndexPath:indexPath].center.x - - self.avatarCollectionView.contentOffset.x; - CGFloat max = self.avatarCollectionView.bounds.size.width; - - [cell setVisibility:MAX(0, MIN( 1, 1 - ABS( current / (max / 2) - 1 ) )) animated:animated]; + [self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO]; } - (void)updateModeForAvatar:(MPAvatarCell *)avatarCell atIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated { @@ -505,6 +500,21 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { } } +- (void)updateVisibilityForAvatar:(MPAvatarCell *)cell atIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated { + + CGFloat current = [self.avatarCollectionView layoutAttributesForItemAtIndexPath:indexPath].center.x - + self.avatarCollectionView.contentOffset.x; + CGFloat max = self.avatarCollectionView.bounds.size.width; + + CGFloat visibility = MAX(0, MIN( 1, 1 - ABS( current / (max / 2) - 1 ) )); + [cell setVisibility:visibility animated:animated]; + + if (cell.newUser) { + self.previousAvatarButton.alpha = cell.mode == MPAvatarModeRaisedAndActive? visibility * 0.7f: 0; + self.nextAvatarButton.alpha = cell.mode == MPAvatarModeRaisedAndActive? visibility * 0.7f: 0; + } +} + - (void)afterUpdatesMainQueue:(void (^)(void))block { [_afterUpdates addOperationWithBlock:^{ @@ -560,6 +570,14 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { Weakify(self); NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady]; + [UIView animateWithDuration:0.3f animations:^{ + self.avatarCollectionView.alpha = mainContext? 1: 0; + }]; + if (mainContext && self.storeLoadingActivity.isAnimating) + [self.storeLoadingActivity stopAnimating]; + if (!mainContext && !self.storeLoadingActivity.isAnimating) + [self.storeLoadingActivity startAnimating]; + if (!_mocObserver && mainContext) _mocObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext @@ -618,7 +636,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { - (void)setActive:(BOOL)active { - [self setActive:active animated:YES]; + [self setActive:active animated:NO]; } - (void)setActive:(BOOL)active animated:(BOOL)animated { @@ -674,7 +692,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { [_afterUpdates setSuspended:YES]; dbg(@"suspend updates"); __block BOOL requestFirstResponder = NO; - [UIView animateWithDuration:animated? 0.3f: 0 animations:^{ + [UIView animateWithDuration:animated? 0.4f: 0 animations:^{ MPAvatarCell *selectedAvatar = [self selectedAvatar]; // Set avatar modes. @@ -682,6 +700,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:0]; MPAvatarCell *avatarCell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath]; [self updateModeForAvatar:avatarCell atIndexPath:indexPath animated:animated]; + [self updateVisibilityForAvatar:avatarCell atIndexPath:indexPath animated:animated]; if (selectedAvatar && avatarCell == selectedAvatar) [self.avatarCollectionView scrollToItemAtIndexPath:indexPath @@ -698,6 +717,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { self.entryLabel.text = strl( @"Enter your master password:" ); self.entryField.text = nil; self.entryField.secureTextEntry = YES; + self.entryField.autocapitalizationType = UITextAutocapitalizationTypeNone; break; } case MPActiveUserStateUserName: { @@ -705,6 +725,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { self.entryLabel.text = strl( @"Enter your full name:" ); self.entryField.text = nil; self.entryField.secureTextEntry = NO; + self.entryField.autocapitalizationType = UITextAutocapitalizationTypeWords; break; } case MPActiveUserStateMasterPasswordChoice: { @@ -712,6 +733,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { self.entryLabel.text = strl( @"Choose your master password:" ); self.entryField.text = nil; self.entryField.secureTextEntry = YES; + self.entryField.autocapitalizationType = UITextAutocapitalizationTypeNone; break; } case MPActiveUserStateMasterPasswordConfirmation: { @@ -720,6 +742,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { self.entryLabel.text = strl( @"Confirm your master password:" ); self.entryField.text = nil; self.entryField.secureTextEntry = YES; + self.entryField.autocapitalizationType = UITextAutocapitalizationTypeNone; break; } case MPActiveUserStateMinimized: @@ -731,7 +754,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { switch (activeUserState) { case MPActiveUserStateNone: { self.navigationBarToTopConstraint.priority = UILayoutPriorityDefaultHigh; - self.avatarCollectionCenterConstraint.priority = UILayoutPriorityDefaultHigh; self.avatarCollectionView.scrollEnabled = YES; self.entryContainer.alpha = 0; self.footerContainer.alpha = 1; @@ -742,7 +764,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { case MPActiveUserStateMasterPasswordChoice: case MPActiveUserStateMasterPasswordConfirmation: { self.navigationBarToTopConstraint.priority = UILayoutPriorityDefaultHigh; - self.avatarCollectionCenterConstraint.priority = UILayoutPriorityDefaultLow; self.avatarCollectionView.scrollEnabled = NO; self.entryContainer.alpha = 1; self.footerContainer.alpha = 1; @@ -751,7 +772,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { } case MPActiveUserStateMinimized: { self.navigationBarToTopConstraint.priority = 1; - self.avatarCollectionCenterConstraint.priority = UILayoutPriorityDefaultLow; self.avatarCollectionView.scrollEnabled = NO; self.entryContainer.alpha = 0; self.footerContainer.alpha = 0; @@ -759,25 +779,29 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { } } [self.navigationBarToTopConstraint apply]; - [self.avatarCollectionCenterConstraint apply]; - - // Toggle the keyboard. } completion:^(BOOL finished) { dbg(@"resume updates"); [_afterUpdates setSuspended:NO]; }]; + UIResponder *oldFirstResponder = [UIResponder findFirstResponder]; if (requestFirstResponder) [self.entryField becomeFirstResponder]; else [self.entryField resignFirstResponder]; + UIResponder *newFirstResponder = [UIResponder findFirstResponder]; + if (newFirstResponder != oldFirstResponder) + dbg(@"first responder: %@ -> %@", oldFirstResponder, newFirstResponder); } #pragma mark - Actions -- (IBAction)doSignOut:(UIBarButtonItem *)sender { +- (IBAction)changeAvatar:(UIButton *)sender { - [[MPiOSAppDelegate get] signOutAnimated:YES]; + if (sender == self.previousAvatarButton) + --[self selectedAvatar].avatar; + if (sender == self.nextAvatarButton) + ++[self selectedAvatar].avatar; } @end diff --git a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m index 17ecf6ed..d31dc4d5 100644 --- a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m +++ b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m @@ -10,25 +10,28 @@ #import "MPAppDelegate_Key.h" #import "MPAppDelegate_Store.h" #import "IASKSettingsReader.h" +#import "JRSwizzle.h" @interface MPiOSAppDelegate() @property(nonatomic, weak) PearlAlert *handleCloudDisabledAlert; @property(nonatomic, weak) PearlAlert *handleCloudContentAlert; @property(nonatomic, weak) PearlAlert *fixCloudContentAlert; -@property(nonatomic, weak) PearlOverlay *storeLoading; +@property(nonatomic, weak) PearlOverlay *storeLoadingOverlay; @end @implementation MPiOSAppDelegate + (void)initialize { - [PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo; + if ([self class] == [MPiOSAppDelegate class]) { + [PearlLogger get].historyLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelTrace: PearlLogLevelInfo; #ifdef DEBUG - [PearlLogger get].printLevel = PearlLogLevelDebug; + [PearlLogger get].printLevel = PearlLogLevelDebug; #else - [PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo; + [PearlLogger get].printLevel = [[MPiOSConfig get].traceMode boolValue]? PearlLogLevelDebug: PearlLogLevelInfo; #endif + } } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { @@ -731,8 +734,8 @@ dispatch_async( dispatch_get_main_queue(), ^{ [self.handleCloudContentAlert cancelAlertAnimated:YES]; - if (![self.storeLoading isVisible]) - self.storeLoading = [PearlOverlay showProgressOverlayWithTitle:@"Loading Sites"]; + if (!self.storeLoadingOverlay) + self.storeLoadingOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Loading Sites"]; } ); [super ubiquityStoreManager:manager willLoadStoreIsCloud:isCloudStore]; @@ -746,21 +749,21 @@ [self.handleCloudContentAlert cancelAlertAnimated:YES]; [self.fixCloudContentAlert cancelAlertAnimated:YES]; - [self.storeLoading cancelOverlayAnimated:YES]; + [self.storeLoadingOverlay cancelOverlayAnimated:YES]; [self.handleCloudDisabledAlert cancelAlertAnimated:YES]; } - (void)ubiquityStoreManager:(UbiquityStoreManager *)manager failedLoadingStoreWithCause:(UbiquityStoreErrorCause)cause context:(id)context wasCloud:(BOOL)wasCloudStore { - [self.storeLoading cancelOverlayAnimated:YES]; + [self.storeLoadingOverlay cancelOverlayAnimated:YES]; [self.handleCloudDisabledAlert cancelAlertAnimated:YES]; } - (BOOL)ubiquityStoreManager:(UbiquityStoreManager *)manager handleCloudContentCorruptionWithHealthyStore:(BOOL)storeHealthy { - if (manager.cloudEnabled && !storeHealthy && !([self.handleCloudContentAlert.alertView isVisible] || [self.fixCloudContentAlert.alertView isVisible])) { - [self.storeLoading cancelOverlayAnimated:YES]; + if (manager.cloudEnabled && !storeHealthy && !(self.handleCloudContentAlert || self.fixCloudContentAlert)) { + [self.storeLoadingOverlay cancelOverlayAnimated:YES]; [self.handleCloudDisabledAlert cancelAlertAnimated:YES]; [self showCloudContentAlert]; }; @@ -770,17 +773,19 @@ - (BOOL)ubiquityStoreManagerHandleCloudDisabled:(UbiquityStoreManager *)manager { - if (![self.handleCloudDisabledAlert isVisible]) + if (!self.handleCloudDisabledAlert) self.handleCloudDisabledAlert = [PearlAlert showAlertWithTitle:@"iCloud Login" message: @"You haven't added an iCloud account to your device yet.\n" - @"To add one, tap 'Wait For Me', go into Apple's Settings and add an iCloud account." + @"To add one, go into Apple's Settings -> iCloud." viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == alert.firstOtherButtonIndex) + if (buttonIndex == alert.firstOtherButtonIndex) { + [MPiOSConfig get].iCloudEnabled = @NO; return; + } [self.storeManager reloadStore]; - } cancelTitle:@"Wait For Me" otherTitles:@"Disable iCloud", nil]; + } cancelTitle:@"Try Again" otherTitles:@"Disable iCloud", nil]; return YES; } diff --git a/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard index 9a26cdea..19122c75 100644 --- a/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/ObjC/iOS/MainStoryboard_iPhone.storyboard @@ -1,7 +1,7 @@ - + diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj index 02a712bf..3a11f7fb 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */; }; 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; }; 93D39233C3EDD9A947ABA52D /* LLButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BF6BCBDFFE844E7D34C /* LLButtonView.m */; }; 93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; }; @@ -14,7 +15,9 @@ 93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; }; 93D39392DEDA376F93C6C718 /* MPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BAA71DE51B4D8A1286C /* MPCell.m */; }; 93D393BA1B8402D08DB40231 /* MPPasswordElementCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39342E5F115EFCC90E976 /* MPPasswordElementCell.m */; }; + 93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; }; 93D394F6D3F6E2553AA0D684 /* MPPasswordLargeStoredCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3947F6BB69CA9A9124A5D /* MPPasswordLargeStoredCell.m */; }; + 93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */; }; 93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */; }; 93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; }; 93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; }; @@ -23,6 +26,8 @@ 93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3942A356B639724157982 /* PearlOverlay.h */; }; 93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */; }; 93D3980046016EFD05B35BC5 /* PearlUICollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */; }; + 93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */; }; + 93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; }; 93D399278165FD6D950F0025 /* MPPasswordTypesCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39097C0AAE62C1C321BFC /* MPPasswordTypesCell.m */; }; 93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; @@ -35,9 +40,11 @@ 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; 93D39C8AD8EAB747856B3A8C /* LLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3923B42DA2DA18F287092 /* LLModel.m */; }; 93D39CB5E2EC1078E898F46A /* MPPasswordLargeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937863061C3916AF7AD2 /* MPPasswordLargeCell.m */; }; + 93D39CB738EB39994D1B691C /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D395268D000379F218BCA1 /* NSManagedObject+Pearl.h */; }; 93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; 93D39EDD960C381D64E4DCDD /* MPPasswordSmallCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3952CC60991B97D69F26A /* MPPasswordSmallCell.m */; }; + 93D39F18B48FB351E727DD2B /* NSManagedObject+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D398EBF99FF0C245FD561E /* NSManagedObject+Pearl.m */; }; 93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A28369954D147E239BA /* MPSetupViewController.m */; }; 93D39FA97F4C3F69A75D5A03 /* MPPasswordLargeGeneratedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3993422E207BF0B21D089 /* MPPasswordLargeGeneratedCell.m */; }; DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */; }; @@ -524,12 +531,15 @@ 93D3932D6C25F2C2D929F8A1 /* MPPasswordElementCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordElementCell.h; sourceTree = ""; }; 93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = ""; }; 93D39342E5F115EFCC90E976 /* MPPasswordElementCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordElementCell.m; sourceTree = ""; }; + 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencySegue.m; sourceTree = ""; }; 93D3937863061C3916AF7AD2 /* MPPasswordLargeCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeCell.m; sourceTree = ""; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = ""; }; 93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = ""; }; 93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; }; 93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = ""; }; + 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+PearlFirstResponder.h"; sourceTree = ""; }; 93D3947F6BB69CA9A9124A5D /* MPPasswordLargeStoredCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeStoredCell.m; sourceTree = ""; }; + 93D395268D000379F218BCA1 /* NSManagedObject+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+Pearl.h"; sourceTree = ""; }; 93D3952CC60991B97D69F26A /* MPPasswordSmallCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordSmallCell.m; sourceTree = ""; }; 93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = ""; }; 93D395BA6B2CFF5F49A4D25F /* MPPasswordLargeStoredCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeStoredCell.h; sourceTree = ""; }; @@ -541,18 +551,24 @@ 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = ""; }; 93D39888EE06F06264CC963B /* MPPasswordSmallCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordSmallCell.h; sourceTree = ""; }; 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = ""; }; + 93D398EBF99FF0C245FD561E /* NSManagedObject+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+Pearl.m"; sourceTree = ""; }; 93D3993422E207BF0B21D089 /* MPPasswordLargeGeneratedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeGeneratedCell.m; sourceTree = ""; }; 93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = ""; }; 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = ""; }; + 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+PearlFirstResponder.m"; sourceTree = ""; }; 93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = ""; }; 93D39A3CC4D8330831FC8CB4 /* LLToggleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLToggleViewController.h; sourceTree = ""; }; + 93D39A41340CF778E00D0E6D /* MPEmergencySegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencySegue.h; sourceTree = ""; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = ""; }; + 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPEmergencyViewController.m; sourceTree = ""; }; 93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = ""; }; 93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = ""; }; 93D39BA6C5CB452973918B7D /* LLButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLButtonView.h; sourceTree = ""; }; 93D39BAA71DE51B4D8A1286C /* MPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCell.m; sourceTree = ""; }; 93D39BF6BCBDFFE844E7D34C /* LLButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLButtonView.m; sourceTree = ""; }; + 93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = ""; }; 93D39C8E26B06F01566785B7 /* LLToggleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLToggleViewController.m; sourceTree = ""; }; + 93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = ""; }; 93D39CE1138FDA4D3D1B847A /* MPPasswordLargeGeneratedCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeGeneratedCell.h; sourceTree = ""; }; 93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = ""; }; 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = ""; }; @@ -560,6 +576,7 @@ 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+PearlAdjustInsets.h"; sourceTree = ""; }; 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordCell.m; sourceTree = ""; }; 93D39E02F69CACAB61C056F8 /* MPPasswordLargeCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeCell.h; sourceTree = ""; }; + 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsSegue.m; sourceTree = ""; }; 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = ""; }; 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = ""; }; DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; @@ -1701,6 +1718,8 @@ DA5BFA45147E415C00F98B1E /* Products */, 93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */, 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */, + 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */, + 93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */, ); sourceTree = ""; }; @@ -2573,6 +2592,10 @@ 93D39888EE06F06264CC963B /* MPPasswordSmallCell.h */, 93D39342E5F115EFCC90E976 /* MPPasswordElementCell.m */, 93D3932D6C25F2C2D929F8A1 /* MPPasswordElementCell.h */, + 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */, + 93D39A41340CF778E00D0E6D /* MPEmergencySegue.h */, + 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */, + 93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */, ); path = iOS; sourceTree = ""; @@ -3108,6 +3131,10 @@ 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */, 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */, 93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */, + 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */, + 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */, + 93D398EBF99FF0C245FD561E /* NSManagedObject+Pearl.m */, + 93D395268D000379F218BCA1 /* NSManagedObject+Pearl.h */, ); path = "Pearl-UIKit"; sourceTree = ""; @@ -3292,6 +3319,8 @@ DAEB934218AA537D000490CC /* bn.h in Headers */, 93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */, 93D3980046016EFD05B35BC5 /* PearlUICollectionView.h in Headers */, + 93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */, + 93D39CB738EB39994D1B691C /* NSManagedObject+Pearl.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3875,6 +3904,9 @@ 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */, 93D39EDD960C381D64E4DCDD /* MPPasswordSmallCell.m in Sources */, 93D393BA1B8402D08DB40231 /* MPPasswordElementCell.m in Sources */, + 93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */, + 93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */, + 93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3955,6 +3987,8 @@ DA2CA4DF18D28859007798F8 /* NSTimer+PearlBlock.m in Sources */, 93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */, 93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */, + 93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */, + 93D39F18B48FB351E727DD2B /* NSManagedObject+Pearl.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme index 9cc7e2ed..c6b79a40 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme @@ -57,6 +57,12 @@ ReferencedContainer = "container:MasterPassword-iOS.xcodeproj"> + + + + diff --git a/MasterPassword/ObjC/iOS/Storyboard.storyboard b/MasterPassword/ObjC/iOS/Storyboard.storyboard index 72696868..b70794b1 100644 --- a/MasterPassword/ObjC/iOS/Storyboard.storyboard +++ b/MasterPassword/ObjC/iOS/Storyboard.storyboard @@ -23,6 +23,10 @@ + + + + @@ -31,7 +35,7 @@ - + @@ -45,6 +49,9 @@ + + + @@ -123,6 +130,36 @@ + + @@ -149,9 +186,38 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -160,6 +226,7 @@ + @@ -233,18 +300,24 @@ + + + + + + @@ -264,13 +337,18 @@ + + + + + @@ -309,7 +387,7 @@ - + @@ -324,37 +402,23 @@ - - - - - - - - - - - - - - - - + + @@ -383,7 +447,11 @@ - + + + + + @@ -395,7 +463,7 @@ - + @@ -844,12 +912,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +