diff --git a/MasterPassword/ObjC/MPAlgorithmV1.m b/MasterPassword/ObjC/MPAlgorithmV1.m index a95db09b..b2bad969 100644 --- a/MasterPassword/ObjC/MPAlgorithmV1.m +++ b/MasterPassword/ObjC/MPAlgorithmV1.m @@ -1,12 +1,12 @@ /** - * 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 - */ +* 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 +*/ // // MPAlgorithmV1 @@ -28,7 +28,7 @@ - (BOOL)migrateElement:(MPElementEntity *)element explicit:(BOOL)explicit { if (element.version != [self version] - 1) - // Only migrate from previous version. + // Only migrate from previous version. return NO; if (!explicit) { @@ -49,16 +49,17 @@ static __strong NSDictionary *MPTypes_ciphers = nil; static dispatch_once_t once = 0; - dispatch_once(&once, ^{ + dispatch_once( &once, ^{ MPTypes_ciphers = [NSDictionary dictionaryWithContentsOfURL: - [[NSBundle mainBundle] URLForResource:@"ciphers" withExtension:@"plist"]]; - }); + [[NSBundle mainBundle] URLForResource:@"ciphers" withExtension:@"plist"]]; + } ); // Determine the seed whose bytes will be used for calculating a password - uint32_t ncounter = htonl(counter), nnameLength = htonl(name.length); - NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof(ncounter)]; - NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof(nnameLength)]; - trc(@"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex], name, [counterBytes encodeHex]); + uint32_t ncounter = htonl( counter ), nnameLength = htonl( name.length ); + NSData *counterBytes = [NSData dataWithBytes:&ncounter length:sizeof( ncounter )]; + NSData *nameLengthBytes = [NSData dataWithBytes:&nnameLength length:sizeof( nnameLength )]; + trc( @"seed from: hmac-sha256(%@, 'com.lyndir.masterpassword' | %@ | %@ | %@)", [key.keyData encodeBase64], [nameLengthBytes encodeHex], + name, [counterBytes encodeHex] ); NSData *seed = [[NSData dataByConcatenatingDatas: [@"com.lyndir.masterpassword" dataUsingEncoding:NSUTF8StringEncoding], nameLengthBytes, @@ -66,17 +67,17 @@ counterBytes, nil] hmacWith:PearlHashSHA256 key:key.keyData]; - trc(@"seed is: %@", [seed encodeBase64]); + trc( @"seed is: %@", [seed encodeBase64] ); const unsigned char *seedBytes = seed.bytes; // Determine the cipher from the first seed byte. - NSAssert([seed length], @"Missing seed."); + NSAssert( [seed length], @"Missing seed." ); NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:[self classNameOfType:type]] valueForKey:[self nameOfType:type]]; NSString *cipher = typeCiphers[seedBytes[0] % [typeCiphers count]]; - trc(@"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher); + trc( @"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher ); // Encode the content, character by character, using subsequent seed bytes and the cipher. - NSAssert([seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher."); + NSAssert( [seed length] >= [cipher length] + 1, @"Insufficient seed bytes to encode cipher." ); NSMutableString *content = [NSMutableString stringWithCapacity:[cipher length]]; for (NSUInteger c = 0; c < [cipher length]; ++c) { uint16_t keyByte = seedBytes[c + 1]; @@ -84,7 +85,7 @@ NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass]; NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length], 1 )]; - trc(@"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character); + trc( @"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character ); [content appendString:character]; } diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.h b/MasterPassword/ObjC/MPAppDelegate_Store.h index edbc2605..96891b53 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.h +++ b/MasterPassword/ObjC/MPAppDelegate_Store.h @@ -31,7 +31,7 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) { - (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context; /** @param completion The block to execute after adding the element, executed from the main thread with the new element in the main MOC. */ -- (void)addElementNamed:(NSString *)siteName completion:(void (^)(MPElementEntity *element))completion; +- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion; - (MPElementEntity *)changeElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context toType:(MPElementType)type; - (MPImportResult)importSites:(NSString *)importedSitesString askImportPassword:(NSString *(^)(NSString *userName))importPassword diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.m b/MasterPassword/ObjC/MPAppDelegate_Store.m index d0d54a64..ede27300 100644 --- a/MasterPassword/ObjC/MPAppDelegate_Store.m +++ b/MasterPassword/ObjC/MPAppDelegate_Store.m @@ -435,10 +435,10 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext #pragma mark - Utilities -- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element))completion { +- (void)addElementNamed:(NSString *)siteName completion:(void ( ^ )(MPElementEntity *element, NSManagedObjectContext *context))completion { if (![siteName length]) { - completion( nil ); + completion( nil, nil ); return; } @@ -446,7 +446,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext MPUserEntity *activeUser = [self activeUserInContext:context]; NSAssert( activeUser, @"Missing user." ); if (!activeUser) { - completion( nil ); + completion( nil, nil ); return; } @@ -466,7 +466,7 @@ PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext [context saveToStore]; - completion( element ); + completion( element, context ); }]; } diff --git a/MasterPassword/ObjC/MPEntities.m b/MasterPassword/ObjC/MPEntities.m index 00d7ee7c..c9b37552 100644 --- a/MasterPassword/ObjC/MPEntities.m +++ b/MasterPassword/ObjC/MPEntities.m @@ -20,11 +20,11 @@ @try { NSError *error = nil; if (!(success = [self save:&error])) - err(@"While saving: %@", error); + err( @"While saving: %@", error ); } @catch (NSException *exception) { success = NO; - err(@"While saving: %@", exception); + err( @"While saving: %@", exception ); } }]; } @@ -115,7 +115,8 @@ - (NSString *)debugDescription { return strf( @"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}", - NSStringFromClass( [self class] ), self.name, self.user.name, (long)self.type, (long)self.uses, self.lastUsed, (long)self.version, + NSStringFromClass( [self class] ), self.name, self.user.name, (long)self.type, (long)self.uses, self.lastUsed, + (long)self.version, self.loginName, self.requiresExplicitMigration ); } @@ -123,9 +124,11 @@ while (self.version < MPAlgorithmDefaultVersion) if ([MPAlgorithmForVersion( self.version + 1 ) migrateElement:self explicit:explicit]) - inf(@"%@ migration to version: %ld succeeded for element: %@", explicit? @"Explicit": @"Automatic", (long)self.version + 1, self); + inf( @"%@ migration to version: %ld succeeded for element: %@", + explicit? @"Explicit": @"Automatic", (long)self.version + 1, self ); else { - wrn(@"%@ migration to version: %ld failed for element: %@", explicit? @"Explicit": @"Automatic", (long)self.version + 1, self); + wrn( @"%@ migration to version: %ld failed for element: %@", + explicit? @"Explicit": @"Automatic", (long)self.version + 1, self ); return NO; } @@ -137,7 +140,7 @@ return [self.algorithm resolveContentForElement:self usingKey:key]; } -- (void)resolveContentUsingKey:(MPKey *)key result:(void (^)(NSString *))result { +- (void)resolveContentUsingKey:(MPKey *)key result:(void ( ^ )(NSString *))result { [self.algorithm resolveContentForElement:self usingKey:key result:result]; } diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m index 8a72102c..4a41a485 100644 --- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m +++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m @@ -229,7 +229,7 @@ switch (returnCode) { case NSAlertFirstButtonReturn: { // "Create" button. - [[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element) { + [[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element, NSManagedObjectContext *context) { if (element) PearlMainQueue( ^{ [self updateElements]; } ); }]; diff --git a/MasterPassword/ObjC/iOS/MPAvatarCell.m b/MasterPassword/ObjC/iOS/MPAvatarCell.m index 8c4481f5..9b16d7d7 100644 --- a/MasterPassword/ObjC/iOS/MPAvatarCell.m +++ b/MasterPassword/ObjC/iOS/MPAvatarCell.m @@ -17,7 +17,6 @@ // #import "MPAvatarCell.h" -#import "MPPasswordLargeCell.h" const long MPAvatarAdd = 10000; @@ -207,10 +206,10 @@ const long MPAvatarAdd = 10000; switch (self.mode) { case MPAvatarModeLowered: { - [self.avatarSizeConstraint layoutWithConstant:self.avatarImageView.image.size.height]; - [self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultLow]; - [self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultLow]; - [self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultLow]; + [[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height] layoutIfNeeded]; + [[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow] layoutIfNeeded]; + [[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow] layoutIfNeeded]; + [[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow] layoutIfNeeded]; self.nameContainer.alpha = self.visibility; self.nameContainer.backgroundColor = [UIColor clearColor]; self.avatarImageView.alpha = self.visibility / 0.7f + 0.3f; @@ -218,10 +217,10 @@ const long MPAvatarAdd = 10000; break; } case MPAvatarModeRaisedButInactive: { - [self.avatarSizeConstraint layoutWithConstant:self.avatarImageView.image.size.height]; - [self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; - [self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultLow]; - [self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultLow]; + [[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height] layoutIfNeeded]; + [[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; + [[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow] layoutIfNeeded]; + [[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow] layoutIfNeeded]; self.nameContainer.alpha = self.visibility; self.nameContainer.backgroundColor = [UIColor clearColor]; self.avatarImageView.alpha = 0; @@ -229,10 +228,10 @@ const long MPAvatarAdd = 10000; break; } case MPAvatarModeRaisedAndActive: { - [self.avatarSizeConstraint layoutWithConstant:self.avatarImageView.image.size.height]; - [self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; - [self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultLow]; - [self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; + [[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height] layoutIfNeeded]; + [[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; + [[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow] layoutIfNeeded]; + [[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; self.nameContainer.alpha = self.visibility; self.nameContainer.backgroundColor = [UIColor blackColor]; self.avatarImageView.alpha = 1; @@ -240,10 +239,10 @@ const long MPAvatarAdd = 10000; break; } case MPAvatarModeRaisedAndHidden: { - [self.avatarSizeConstraint layoutWithConstant:self.avatarImageView.image.size.height]; - [self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; - [self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultLow]; - [self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; + [[self.avatarSizeConstraint updateConstant:self.avatarImageView.image.size.height] layoutIfNeeded]; + [[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; + [[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow] layoutIfNeeded]; + [[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; self.nameContainer.alpha = 0; self.nameContainer.backgroundColor = [UIColor blackColor]; self.avatarImageView.alpha = 0; @@ -251,10 +250,10 @@ const long MPAvatarAdd = 10000; break; } case MPAvatarModeRaisedAndMinimized: { - [self.avatarSizeConstraint layoutWithConstant:36]; - [self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultLow]; - [self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; - [self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; + [[self.avatarSizeConstraint updateConstant:36] layoutIfNeeded]; + [[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow] layoutIfNeeded]; + [[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; + [[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; self.nameContainer.alpha = 0; self.nameContainer.backgroundColor = [UIColor blackColor]; self.avatarImageView.alpha = 1; diff --git a/MasterPassword/ObjC/iOS/MPPasswordCell.h b/MasterPassword/ObjC/iOS/MPPasswordCell.h index 5eba3dbe..0cbc7705 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordCell.h +++ b/MasterPassword/ObjC/iOS/MPPasswordCell.h @@ -20,9 +20,15 @@ #import "MPEntities.h" #import "MPCell.h" -@interface MPPasswordCell : MPCell +typedef NS_ENUM ( NSUInteger, MPPasswordCellMode ) { + MPPasswordCellModePassword, + MPPasswordCellModeSettings, +}; -/** Populate our UI to reflect the current state. */ -- (void)updateAnimated:(BOOL)animated; +@interface MPPasswordCell : MPCell + +- (void)setElement:(MPElementEntity *)element animated:(BOOL)animated; +- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated; +- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated; @end diff --git a/MasterPassword/ObjC/iOS/MPPasswordCell.m b/MasterPassword/ObjC/iOS/MPPasswordCell.m index 2c69f422..f66f6e05 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordCell.m +++ b/MasterPassword/ObjC/iOS/MPPasswordCell.m @@ -1,12 +1,12 @@ /** - * 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 - */ +* 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 +*/ // // MPAvatarCell.h @@ -20,14 +20,62 @@ #import "MPiOSAppDelegate.h" #import "MPAppDelegate_Store.h" -@implementation MPPasswordCell + +// return [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:self.type]; + + +@interface MPPasswordCell() + +@property(nonatomic, strong) IBOutlet UILabel *siteNameLabel; +@property(nonatomic, strong) IBOutlet UITextField *passwordField; +@property(nonatomic, strong) IBOutlet UITextField *loginNameField; +@property(nonatomic, strong) IBOutlet UIPageControl *pageControl; +@property(nonatomic, strong) IBOutlet UILabel *strengthLabel; +@property(nonatomic, strong) IBOutlet UILabel *counterLabel; +@property(nonatomic, strong) IBOutlet UIButton *counterButton; +@property(nonatomic, strong) IBOutlet UIButton *upgradeButton; +@property(nonatomic, strong) IBOutlet UIButton *modeButton; +@property(nonatomic, strong) IBOutlet UIButton *passwordEditButton; +@property(nonatomic, strong) IBOutlet UIButton *usernameEditButton; +@property(nonatomic, strong) IBOutlet UIScrollView *modeScrollView; +@property(nonatomic, strong) IBOutlet UIButton *selectionButton; + +@property(nonatomic) MPPasswordCellMode mode; +@property(nonatomic, copy) NSString *transientSite; + +@end + +@implementation MPPasswordCell { + NSManagedObjectID *_elementOID; +} #pragma mark - Life cycle -- (void)prepareForReuse { +- (void)awakeFromNib { - [super prepareForReuse]; - [self updateAnimated:NO]; + [super awakeFromNib]; + + [self addGestureRecognizer: + [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doRevealPassword: )]]; + [self.counterButton addGestureRecognizer: + [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doResetCounter: )]]; + + self.selectionButton.layer.cornerRadius = 5; + self.selectionButton.layer.shadowOffset = CGSizeZero; + self.selectionButton.layer.shadowRadius = 5; + self.selectionButton.layer.shadowOpacity = 0; + self.selectionButton.layer.shadowColor = [UIColor whiteColor].CGColor; + + self.pageControl.transform = CGAffineTransformMakeScale( 0.4f, 0.4f ); + + [self.selectionButton observeKeyPath:@"highlighted" + withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) { + button.layer.shadowOpacity = button.selected? 1: button.highlighted? 0.3f: 0; + }]; + [self.selectionButton observeKeyPath:@"selected" + withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) { + button.layer.shadowOpacity = button.selected? 1: button.highlighted? 0.3f: 0; + }]; } // Unblocks animations for all CALayer properties (eg. shadowOpacity) @@ -37,23 +85,204 @@ if (defaultAction == (id)[NSNull null] && [event isEqualToString:@"position"]) return defaultAction; - return NSNullToNil(defaultAction); + return NSNullToNil( defaultAction ); } -#pragma mark - Properties +#pragma mark - State -- (void)setSelected:(BOOL)selected { +- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated { - [super setSelected:selected]; + if (mode == _mode) + return; + + _mode = mode; + [self updateAnimated:animated]; +} + +- (void)setElement:(MPElementEntity *)element animated:(BOOL)animated { + + _elementOID = [element objectID]; + [self updateAnimated:animated]; +} + +- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated { + + self.transientSite = siteName; + [self updateAnimated:animated]; +} + +#pragma mark - UITextFieldDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + + [textField resignFirstResponder]; + return YES; +} + +- (void)textFieldDidBeginEditing:(UITextField *)textField { + + UICollectionView *collectionView = [UICollectionView findAsSuperviewOf:self]; + [collectionView scrollToItemAtIndexPath:[collectionView indexPathForCell:self] + atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; +} + +- (void)textFieldDidEndEditing:(UITextField *)textField { + + if (textField == self.passwordField) { + NSString *text = textField.text; + textField.enabled = NO; + + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + MPElementEntity *element = [self elementInContext:context]; + if (!element) + return; + + if (textField == self.passwordField) { + [element.algorithm saveContent:text toElement:element usingKey:[MPiOSAppDelegate get].key]; + [PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2]; + } + else if (textField == self.loginNameField) { + element.loginName = text; + [PearlOverlay showTemporaryOverlayWithTitle:@"Login Updated" dismissAfter:2]; + } + + [context saveToStore]; + [self updateAnimated:YES]; + }]; + } +} + +#pragma mark - Actions + +- (IBAction)doEditPassword:(UIButton *)sender { + + self.passwordField.enabled = YES; + [self.passwordField becomeFirstResponder]; +} + +- (IBAction)doEditLoginName:(UIButton *)sender { + + self.loginNameField.enabled = YES; + [self.loginNameField becomeFirstResponder]; +} + +- (IBAction)doMode:(UIButton *)sender { + + switch (self.mode) { + case MPPasswordCellModePassword: + [self setMode:MPPasswordCellModeSettings animated:YES]; + break; + case MPPasswordCellModeSettings: + [self setMode:MPPasswordCellModePassword animated:YES]; + break; + } [self updateAnimated:YES]; } -- (void)setHighlighted:(BOOL)highlighted { +- (IBAction)doUpgrade:(UIButton *)sender { - [super setHighlighted:highlighted]; + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + if (![[self elementInContext:context] migrateExplicitly:YES]) { + [PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2]; + return; + } - [self updateAnimated:YES]; + [context saveToStore]; + [PearlOverlay showTemporaryOverlayWithTitle:@"Site Upgraded" dismissAfter:2]; + [self updateAnimated:YES]; + }]; +} + +- (IBAction)doIncrementCounter:(UIButton *)sender { + + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + MPElementEntity *element = [self elementInContext:context]; + if (!element || ![element isKindOfClass:[MPElementGeneratedEntity class]]) + return; + + ++((MPElementGeneratedEntity *)element).counter; + [context saveToStore]; + + [PearlOverlay showTemporaryOverlayWithTitle:@"Generating New Password" dismissAfter:2]; + [self updateAnimated:YES]; + }]; +} + +- (IBAction)doRevealPassword:(UILongPressGestureRecognizer *)recognizer { + + if (recognizer.state != UIGestureRecognizerStateBegan) + return; + + if (self.passwordField.secureTextEntry) { + self.passwordField.secureTextEntry = NO; + + PearlMainQueueAfter( 3, ^{ + self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue]; + } ); + } +} + +- (IBAction)doResetCounter:(UILongPressGestureRecognizer *)recognizer { + + if (recognizer.state != UIGestureRecognizerStateBegan) + return; + + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + MPElementEntity *element = [self elementInContext:context]; + if (!element || ![element isKindOfClass:[MPElementGeneratedEntity class]]) + return; + + ((MPElementGeneratedEntity *)element).counter = 1; + [context saveToStore]; + + [PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2]; + [self updateAnimated:YES]; + }]; +} + +- (IBAction)doUse:(id)sender { + + self.selectionButton.selected = YES; + + if (self.transientSite) { + [[UIResponder findFirstResponder] resignFirstResponder]; + [PearlAlert showAlertWithTitle:@"Create Site" + message:strf( @"Remember site named:\n%@", self.transientSite ) + viewStyle:UIAlertViewStyleDefault + initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { + if (buttonIndex == [alert cancelButtonIndex]) { + self.selectionButton.selected = NO; + return; + } + + [[MPiOSAppDelegate get] + addElementNamed:self.transientSite completion:^(MPElementEntity *element, NSManagedObjectContext *context) { + [self copyContentOfElement:element saveInContext:context]; + PearlMainQueue( ^{ + self.selectionButton.selected = NO; + } ); + }]; + } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; + return; + } + + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + [self copyContentOfElement:[self elementInContext:context] saveInContext:context]; + PearlMainQueue( ^{ + self.selectionButton.selected = NO; + } ); + }]; +} + +#pragma mark - UIScrollViewDelegate + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + + if (roundf( scrollView.contentOffset.x / self.bounds.size.width ) == 0.0f) + [self setMode:MPPasswordCellModePassword animated:YES]; + else + [self setMode:MPPasswordCellModeSettings animated:YES]; } #pragma mark - Private @@ -68,8 +297,100 @@ } [UIView animateWithDuration:animated? 0.3f: 0 animations:^{ - self.layer.shadowOpacity = self.selected? 1: self.highlighted? 0.3f: 0; + MPElementEntity *mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; + + // UI + self.selectionButton.layer.shadowOpacity = self.selectionButton.selected? 1: self.selectionButton.highlighted? 0.3f: 0; + self.upgradeButton.alpha = mainElement.requiresExplicitMigration? 1: 0; + self.passwordEditButton.alpha = self.transientSite || mainElement.type & MPElementTypeClassGenerated? 0: 1; + self.modeButton.alpha = self.transientSite? 0: 1; + self.counterLabel.alpha = self.counterButton.alpha = mainElement.type & MPElementTypeClassGenerated? 1: 0; + self.modeButton.selected = self.mode == MPPasswordCellModeSettings; + self.pageControl.currentPage = self.mode == MPPasswordCellModePassword? 0: 1; + [self.modeScrollView setContentOffset:CGPointMake( self.mode * self.modeScrollView.frame.size.width, 0 ) animated:YES]; + + // Site Name + self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainElement.name, + self.transientSite? @"Tap to create": [mainElement.algorithm shortNameOfType:mainElement.type] ); + + // Site Password + self.passwordField.enabled = NO; + self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue]; + self.passwordField.attributedPlaceholder = stra( + mainElement.type & MPElementTypeClassStored? strl( @"Set custom password" ): + mainElement.type & MPElementTypeClassGenerated? strl( @"Generating..." ): @"", @{ + NSForegroundColorAttributeName : [UIColor whiteColor] + } ); + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + NSString *password; + if (self.transientSite) + password = [MPAlgorithmDefault generateContentNamed:self.transientSite ofType: + [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPElementTypeGeneratedLong + withCounter:1 usingKey:[MPiOSAppDelegate get].key]; + else + password = [[self elementInContext:context] resolveContentUsingKey:[MPiOSAppDelegate get].key]; + + PearlMainQueue( ^{ + self.passwordField.text = password; + } ); + }]; + + // Site Counter + if ([mainElement isKindOfClass:[MPElementGeneratedEntity class]]) + self.counterLabel.text = strf( @"%lu", (unsigned long)((MPElementGeneratedEntity *)mainElement).counter ); + + // Site Login Name + self.loginNameField.enabled = NO; + self.loginNameField.text = mainElement.loginName; + self.loginNameField.attributedPlaceholder = stra( strl( @"Set login name" ), @{ + NSForegroundColorAttributeName : [UIColor whiteColor] + } ); + + // Strength Label +//#warning TODO }]; } +- (void)copyContentOfElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { + + // Copy content. + switch (self.mode) { + case MPPasswordCellModePassword: { + inf( @"Copying password for: %@", element.name ); + NSString *password = [element resolveContentUsingKey:[MPAppDelegate_Shared get].key]; + if (![password length]) + return; + + PearlMainQueue( ^{ + [PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2]; + [UIPasteboard generalPasteboard].string = password; + } ); + + [element use]; + [context saveToStore]; + break; + } + case MPPasswordCellModeSettings: { + inf( @"Copying login for: %@", element.name ); + NSString *loginName = element.loginName; + if (![loginName length]) + return; + + PearlMainQueue( ^{ + [PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2]; + [UIPasteboard generalPasteboard].string = loginName; + } ); + + [element use]; + [context saveToStore]; + break; + } + } +} + +- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context { + + return [MPElementEntity existingObjectWithID:_elementOID inContext:context]; +} + @end diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeCell.h b/MasterPassword/ObjC/iOS/MPPasswordLargeCell.h deleted file mode 100644 index b2daa321..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeCell.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 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 - */ - -// -// MPAvatarCell.h -// MPAvatarCell -// -// Created by lhunath on 2014-03-11. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import -#import "MPEntities.h" -#import "MPCell.h" -#import "MPPasswordCell.h" - -typedef NS_ENUM (NSUInteger, MPContentFieldMode) { - MPContentFieldModePassword, - MPContentFieldModeUser, -}; - -@interface MPPasswordLargeCell : MPPasswordCell - -@property(nonatomic) MPElementType type; -@property(nonatomic) MPContentFieldMode contentFieldMode; -@property(nonatomic, strong) IBOutlet UILabel *typeLabel; -@property(nonatomic, strong) IBOutlet UITextField *contentField; -@property(nonatomic, strong) IBOutlet UIButton *upgradeButton; -@property(nonatomic, strong) IBOutlet UILabel *nameLabel; -@property(nonatomic, strong) IBOutlet UIButton *loginButton; - -+ (instancetype)dequeueCellWithType:(MPElementType)type fromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath; - -- (void)update; -- (void)updateWithElement:(MPElementEntity *)mainElement; -- (void)updateWithTransientSite:(NSString *)siteName; - -- (void)resolveContentOfCellTypeForTransientSite:(NSString *)siteName usingKey:(MPKey *)key result:(void (^)(NSString *result))resultBlock; -- (void)resolveContentOfCellTypeForElement:(MPElementEntity *)element usingKey:(MPKey *)key result:(void (^)(NSString *result))resultBlock; - -- (void)willBeginDragging; -- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context; - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m b/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m deleted file mode 100644 index bf949b4f..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m +++ /dev/null @@ -1,271 +0,0 @@ -/** -* 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 -*/ - -// -// MPAvatarCell.h -// MPAvatarCell -// -// Created by lhunath on 2014-03-11. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import "MPPasswordLargeCell.h" -#import "MPiOSAppDelegate.h" -#import "MPAppDelegate_Store.h" -#import "MPPasswordLargeGeneratedCell.h" -#import "MPPasswordLargeStoredCell.h" -#import "MPPasswordTypesCell.h" - -@implementation MPPasswordLargeCell - -#pragma mark - Life - -+ (instancetype)dequeueCellWithType:(MPElementType)type fromCollectionView:(UICollectionView *)collectionView - atIndexPath:(NSIndexPath *)indexPath { - - NSAssert( type != 0 && type != (MPElementType)NSNotFound, @"Cannot dequeue a password cell without a type." ); - - NSString *reuseIdentifier; - if (type & MPElementTypeClassGenerated) - reuseIdentifier = NSStringFromClass( [MPPasswordLargeGeneratedCell class] ); - else if (type & MPElementTypeClassStored) - reuseIdentifier = NSStringFromClass( [MPPasswordLargeStoredCell class] ); - else - Throw( @"Unexpected password type: %@", [MPAlgorithmDefault nameOfType:type] ); - - MPPasswordLargeCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; - cell.type = type; - - return cell; -} - -- (void)awakeFromNib { - - [super awakeFromNib]; - - self.layer.cornerRadius = 5; - self.layer.shadowOffset = CGSizeZero; - self.layer.shadowRadius = 5; - self.layer.shadowOpacity = 0; - self.layer.shadowColor = [UIColor whiteColor].CGColor; - - [self addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( didLongPress: )]]; - - [self prepareForReuse]; -} - -- (void)didLongPress:(UILongPressGestureRecognizer *)recognizer { - - if (recognizer.state != UIGestureRecognizerStateBegan) - return; - - if (self.contentField.secureTextEntry) { - self.contentField.secureTextEntry = NO; - PearlMainQueueAfter( 3, ^{ - self.contentField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue]; - } ); - } -} - -- (void)prepareForReuse { - - _contentFieldMode = 0; - self.contentField.text = nil; - - [super prepareForReuse]; -} - -- (void)update { - - self.loginButton.alpha = 0; - self.upgradeButton.alpha = 0; - self.nameLabel.text = @""; - self.typeLabel.text = @""; - self.contentField.text = @""; - self.contentField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue]; - self.contentField.attributedPlaceholder = nil; - self.contentField.enabled = self.contentFieldMode == MPContentFieldModeUser; - self.loginButton.selected = self.contentFieldMode == MPContentFieldModeUser; - - switch (self.contentFieldMode) { - case MPContentFieldModePassword: { - self.contentField.keyboardType = UIKeyboardTypeDefault; - if (self.type & MPElementTypeClassStored) - self.contentField.attributedPlaceholder = stra( strl( @"Set custom password" ), @{ - NSForegroundColorAttributeName : [UIColor whiteColor] - } ); - else if (self.type & MPElementTypeClassGenerated) - self.contentField.attributedPlaceholder = stra( strl( @"Generating..." ), @{ - NSForegroundColorAttributeName : [UIColor whiteColor] - } ); - break; - } - case MPContentFieldModeUser: { - self.contentField.keyboardType = UIKeyboardTypeEmailAddress; - self.contentField.attributedPlaceholder = stra( strl( @"Enter your login name" ), @{ - NSForegroundColorAttributeName : [UIColor whiteColor] - } ); - break; - } - } -} - -- (void)updateWithTransientSite:(NSString *)siteName { - - [self update]; - - self.nameLabel.text = strl( @"%@ - Tap to create", siteName ); - self.typeLabel.text = [MPAlgorithmDefault nameOfType:self.type]; - - [self resolveContentOfCellTypeForTransientSite:siteName usingKey:[MPiOSAppDelegate get].key result:^(NSString *result) { - PearlMainQueue( ^{ self.contentField.text = result; } ); - }]; -} - -- (void)updateWithElement:(MPElementEntity *)mainElement { - - [self update]; - - if (!mainElement) - return; - - self.loginButton.alpha = 1; - if (mainElement.requiresExplicitMigration) - self.upgradeButton.alpha = 1; - - self.nameLabel.text = mainElement.name; - if (self.type == (MPElementType)NSNotFound) - self.typeLabel.text = @"Delete"; - else - self.typeLabel.text = [mainElement.algorithm nameOfType:self.type]; - - switch (self.contentFieldMode) { - case MPContentFieldModePassword: { - MPKey *key = [MPiOSAppDelegate get].key; - if (self.type == mainElement.type) - [mainElement resolveContentUsingKey:key result:^(NSString *result) { - PearlMainQueue( ^{ self.contentField.text = result; } ); - }]; - else - [self resolveContentOfCellTypeForElement:mainElement usingKey:key result:^(NSString *result) { - PearlMainQueue( ^{ self.contentField.text = result; } ); - }]; - break; - } - case MPContentFieldModeUser: { - self.contentField.text = mainElement.loginName; - break; - } - } -} - -- (void)resolveContentOfCellTypeForTransientSite:(NSString *)siteName usingKey:(MPKey *)key result:(void ( ^ )(NSString *))resultBlock { - - resultBlock( nil ); -} - -- (void)resolveContentOfCellTypeForElement:(MPElementEntity *)element usingKey:(MPKey *)key result:(void ( ^ )(NSString *))resultBlock { - - resultBlock( nil ); -} - -- (void)willBeginDragging { -} - -- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { - - return [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:self.type]; -} - -#pragma mark - UITextFieldDelegate - -- (BOOL)textFieldShouldReturn:(UITextField *)textField { - - [textField resignFirstResponder]; - return YES; -} - -- (void)textFieldDidBeginEditing:(UITextField *)textField { - - UICollectionView *collectionView = [UICollectionView findAsSuperviewOf:self]; - [collectionView scrollToItemAtIndexPath:[collectionView indexPathForCell:self] - atScrollPosition:UICollectionViewScrollPositionTop animated:YES]; -} - -- (void)textFieldDidEndEditing:(UITextField *)textField { - - if (textField == self.contentField) { - NSString *newContent = textField.text; - textField.enabled = NO; - - if (self.contentFieldMode == MPContentFieldModeUser) - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPElementEntity *element = [[MPPasswordTypesCell findAsSuperviewOf:self] elementInContext:context]; - if (!element) - return; - - element.loginName = newContent; - [context saveToStore]; - - PearlMainQueue( ^{ - [self updateAnimated:YES]; - [PearlOverlay showTemporaryOverlayWithTitle:@"Login Updated" dismissAfter:2]; - } ); - }]; - } -} - -#pragma mark - Actions - -- (IBAction)doUser:(id)sender { - - switch (self.contentFieldMode) { - case MPContentFieldModePassword: { - self.contentFieldMode = MPContentFieldModeUser; - break; - } - case MPContentFieldModeUser: { - self.contentFieldMode = MPContentFieldModePassword; - break; - } - } -} - -- (IBAction)doUpgrade:(UIButton *)sender { - - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - if ([[[MPPasswordTypesCell findAsSuperviewOf:self] elementInContext:context] migrateExplicitly:YES]) { - [context saveToStore]; - - PearlMainQueue( ^{ - [[MPPasswordTypesCell findAsSuperviewOf:self] reloadData]; - [PearlOverlay showTemporaryOverlayWithTitle:@"Site Upgraded" dismissAfter:2]; - } ); - } - else - PearlMainQueue( ^{ - [PearlOverlay showTemporaryOverlayWithTitle:@"Site Not Upgraded" dismissAfter:2]; - } ); - }]; -} - -#pragma mark - Properties - -- (void)setContentFieldMode:(MPContentFieldMode)contentFieldMode { - - if (_contentFieldMode == contentFieldMode) - return; - - _contentFieldMode = contentFieldMode; - - [[MPPasswordTypesCell findAsSuperviewOf:self] reloadData:self]; -} - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.h b/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.h deleted file mode 100644 index 93de5523..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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 - */ - -// -// MPPasswordLargeDeleteCell.h -// MPPasswordLargeDeleteCell -// -// Created by lhunath on 2014-03-19. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import -#import "MPPasswordLargeCell.h" - -@interface MPPasswordLargeDeleteCell : MPPasswordLargeCell - -+ (MPPasswordLargeCell *)dequeueCellFromCollectionView:(UICollectionView *)view atIndexPath:(NSIndexPath *)path; -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.m b/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.m deleted file mode 100644 index 05db27a5..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.m +++ /dev/null @@ -1,39 +0,0 @@ -/** -* 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 -*/ - -// -// MPPasswordLargeDeleteCell.h -// MPPasswordLargeDeleteCell -// -// Created by lhunath on 2014-03-19. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import "MPPasswordLargeDeleteCell.h" - -@implementation MPPasswordLargeDeleteCell - -#pragma mark - Lifecycle - -+ (MPPasswordLargeCell *)dequeueCellFromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath { - - MPPasswordLargeCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass( [self class] ) - forIndexPath:indexPath]; - cell.type = (MPElementType)NSNotFound; - - return cell; -} - -- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { - - return element; -} - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeGeneratedCell.h b/MasterPassword/ObjC/iOS/MPPasswordLargeGeneratedCell.h deleted file mode 100644 index 34d6b655..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeGeneratedCell.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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 - */ - -// -// MPPasswordLargeGeneratedCell.h -// MPPasswordLargeGeneratedCell -// -// Created by lhunath on 2014-03-19. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import -#import "MPPasswordLargeCell.h" - -@interface MPPasswordLargeGeneratedCell : MPPasswordLargeCell - -@property(strong, nonatomic) IBOutlet UILabel *strengthLabel; -@property(strong, nonatomic) IBOutlet UILabel *counterLabel; -@property(strong, nonatomic) IBOutlet UIButton *counterButton; - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeGeneratedCell.m b/MasterPassword/ObjC/iOS/MPPasswordLargeGeneratedCell.m deleted file mode 100644 index 0ac1466a..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeGeneratedCell.m +++ /dev/null @@ -1,200 +0,0 @@ -/** -* 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 -*/ - -// -// MPPasswordLargeGeneratedCell.h -// MPPasswordLargeGeneratedCell -// -// Created by lhunath on 2014-03-19. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import "MPPasswordLargeGeneratedCell.h" -#import "MPiOSAppDelegate.h" -#import "MPAppDelegate_Store.h" -#import "MPPasswordTypesCell.h" - -@interface MPPasswordLargeGeneratedCell() - -@property(nonatomic, weak) NSTimer *hideStrengthTimer; -@end - -@implementation MPPasswordLargeGeneratedCell - -- (void)awakeFromNib { - - [super awakeFromNib]; - - UILongPressGestureRecognizer *gestureRecognizer = [[UILongPressGestureRecognizer alloc] - initWithTarget:self action:@selector( doResetCounterRecognizer: )]; - [self.counterButton addGestureRecognizer:gestureRecognizer]; -} - -- (void)prepareForReuse { - - [super prepareForReuse]; - - self.strengthLabel.alpha = 0; -} - -- (void)willBeginDragging { - - [super willBeginDragging]; - - [UIView animateWithDuration:0.3f animations:^{ - self.strengthLabel.alpha = 1; - }]; - - [self.hideStrengthTimer invalidate]; - self.hideStrengthTimer = [NSTimer scheduledTimerWithTimeInterval:1 block:^(NSTimer *timer) { - [UIView animateWithDuration:0.3f animations:^{ - self.strengthLabel.alpha = 0; - }]; - } repeats:NO]; -} - -- (void)update { - - [super update]; - - self.counterLabel.alpha = self.counterButton.alpha = 0; -} - -- (void)updateWithElement:(MPElementEntity *)mainElement { - - [super updateWithElement:mainElement]; - - MPElementGeneratedEntity *generatedElement = [self generatedElement:mainElement]; - if (generatedElement) - self.counterLabel.text = strf( @"%lu", (unsigned long)generatedElement.counter ); - else - self.counterLabel.text = @"1"; - - if (mainElement && !mainElement.requiresExplicitMigration) - self.counterLabel.alpha = self.counterButton.alpha = 1; -} - -- (void)resolveContentOfCellTypeForTransientSite:(NSString *)siteName usingKey:(MPKey *)key result:(void ( ^ )(NSString *))resultBlock { - - PearlNotMainQueue( ^{ - resultBlock( [MPAlgorithmDefault generateContentNamed:siteName ofType:self.type withCounter:1 usingKey:key] ); - } ); -} - -- (void)resolveContentOfCellTypeForElement:(MPElementEntity *)element usingKey:(MPKey *)key result:(void ( ^ )(NSString *))resultBlock { - - id algorithm = element.algorithm; - NSString *siteName = element.name; - - PearlNotMainQueue( ^{ - resultBlock( [algorithm generateContentNamed:siteName ofType:self.type withCounter:1 usingKey:key] ); - } ); -} - -- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { - - element = [super saveContentTypeWithElement:element saveInContext:context]; - - MPElementGeneratedEntity *generatedElement = [self generatedElement:element]; - if (generatedElement) { - generatedElement.counter = [self.counterLabel.text intValue]; - [context saveToStore]; - } - - return element; -} - -#pragma mark - Actions - -- (IBAction)doIncrementCounter:(UIButton *)sender { - - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPElementGeneratedEntity *generatedElement = [self generatedElementInContext:context]; - if (!generatedElement) - return; - - ++generatedElement.counter; - [context saveToStore]; - - PearlMainQueue( ^{ - [self updateAnimated:YES]; - [PearlOverlay showTemporaryOverlayWithTitle:@"Counter Incremented" dismissAfter:2]; - } ); - }]; -} - -- (void)doResetCounterRecognizer:(UILongPressGestureRecognizer *)gestureRecognizer { - - if (gestureRecognizer.state != UIGestureRecognizerStateBegan) - return; - - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPElementGeneratedEntity *generatedElement = [self generatedElementInContext:context]; - if (!generatedElement) - return; - - generatedElement.counter = 1; - [context saveToStore]; - - PearlMainQueue( ^{ - [self updateAnimated:YES]; - [PearlOverlay showTemporaryOverlayWithTitle:@"Counter Reset" dismissAfter:2]; - } ); - }]; -} - -#pragma mark - Properties - -- (void)setType:(MPElementType)type { - - [super setType:type]; - - switch (type) { - case MPElementTypeGeneratedMaximum: - self.strengthLabel.text = @"422 quintillion years"; - break; - case MPElementTypeGeneratedLong: - self.strengthLabel.text = @"1.4 years"; - break; - case MPElementTypeGeneratedMedium: - self.strengthLabel.text = @"2 seconds"; - break; - case MPElementTypeGeneratedBasic: - self.strengthLabel.text = @"trivial"; - break; - case MPElementTypeGeneratedShort: - self.strengthLabel.text = @"trivial"; - break; - case MPElementTypeGeneratedPIN: - self.strengthLabel.text = @"trivial"; - break; - case MPElementTypeStoredPersonal: - self.strengthLabel.text = @""; - break; - case MPElementTypeStoredDevicePrivate: - self.strengthLabel.text = @""; - break; - } -} - -- (MPElementGeneratedEntity *)generatedElementInContext:(NSManagedObjectContext *)context { - - return [self generatedElement:[[MPPasswordTypesCell findAsSuperviewOf:self] elementInContext:context]]; -} - -- (MPElementGeneratedEntity *)generatedElement:(MPElementEntity *)element { - - if (![element isKindOfClass:[MPElementGeneratedEntity class]]) - return nil; - - return (MPElementGeneratedEntity *)element; -} - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeStoredCell.h b/MasterPassword/ObjC/iOS/MPPasswordLargeStoredCell.h deleted file mode 100644 index 3f8e4da4..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeStoredCell.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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 - */ - -// -// MPPasswordLargeStoredCell.h -// MPPasswordLargeStoredCell -// -// Created by lhunath on 2014-03-19. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import -#import "MPPasswordLargeCell.h" - -@interface MPPasswordLargeStoredCell : MPPasswordLargeCell - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeStoredCell.m b/MasterPassword/ObjC/iOS/MPPasswordLargeStoredCell.m deleted file mode 100644 index 83757f87..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordLargeStoredCell.m +++ /dev/null @@ -1,102 +0,0 @@ -/** -* 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 -*/ - -// -// MPPasswordLargeGeneratedCell.h -// MPPasswordLargeGeneratedCell -// -// Created by lhunath on 2014-03-19. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import "MPPasswordLargeStoredCell.h" -#import "MPiOSAppDelegate.h" -#import "MPAppDelegate_Store.h" -#import "MPPasswordTypesCell.h" - -@interface MPPasswordLargeStoredCell() - -@property(strong, nonatomic) IBOutlet UIButton *editButton; - -@end - -@implementation MPPasswordLargeStoredCell - -#pragma mark - Lifecycle - -- (void)resolveContentOfCellTypeForElement:(MPElementEntity *)element usingKey:(MPKey *)key result:(void ( ^ )(NSString *))resultBlock { - - if (element.type & MPElementTypeClassStored) - [element resolveContentUsingKey:key result:resultBlock]; - else - [super resolveContentOfCellTypeForElement:element usingKey:key result:resultBlock]; -} - -- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context { - - element = [super saveContentTypeWithElement:element saveInContext:context]; - MPElementStoredEntity *storedElement = [self storedElement:element]; - [storedElement.algorithm saveContent:self.contentField.text toElement:storedElement usingKey:[MPiOSAppDelegate get].key]; - [context saveToStore]; - - return element; -} - -#pragma mark - Actions - -- (IBAction)doEditContent:(UIButton *)sender { - - UITextField *textField = self.contentField; - textField.enabled = YES; - [textField becomeFirstResponder]; -} - -#pragma mark - UITextFieldDelegate - -- (void)textFieldDidEndEditing:(UITextField *)textField { - - [super textFieldDidEndEditing:textField]; - - if (textField == self.contentField) { - NSString *newContent = textField.text; - - if (self.contentFieldMode == MPContentFieldModePassword) - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPElementStoredEntity *storedElement = [self storedElementInContext:context]; - if (!storedElement) - return; - - [storedElement.algorithm saveContent:newContent toElement:storedElement usingKey:[MPiOSAppDelegate get].key]; - [context saveToStore]; - - PearlMainQueue( ^{ - [self updateAnimated:YES]; - [PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2]; - } ); - }]; - } -} - -#pragma mark - Properties - -- (MPElementStoredEntity *)storedElementInContext:(NSManagedObjectContext *)context { - - return [self storedElement:[[MPPasswordTypesCell findAsSuperviewOf:self] elementInContext:context]]; -} - -- (MPElementStoredEntity *)storedElement:(MPElementEntity *)element { - - if (![element isKindOfClass:[MPElementStoredEntity class]]) - return nil; - - return (MPElementStoredEntity *)element; -} - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.h b/MasterPassword/ObjC/iOS/MPPasswordTypesCell.h deleted file mode 100644 index 25f19da8..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 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 - */ - -// -// MPPasswordTypesCell.h -// MPPasswordTypesCell -// -// Created by lhunath on 2014-03-27. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import -#import "MPCell.h" -#import "MPPasswordCell.h" -#import "MPPasswordsViewController.h" -#import "MPPasswordLargeCell.h" - -@interface MPPasswordTypesCell : MPPasswordCell - -@property(nonatomic, strong) IBOutlet UICollectionView *contentCollectionView; - -@property(nonatomic, weak) MPPasswordsViewController *passwordsViewController; -@property(nonatomic, copy) NSString *transientSite; - -@property(nonatomic, strong) id algorithm; -@property(nonatomic) MPElementType activeType; - -- (MPElementEntity *)mainElement; -- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context; -- (void)setElement:(MPElementEntity *)element; -- (void)reloadData; -- (void)reloadData:(MPPasswordLargeCell *)cell; - -+ (instancetype)dequeueCellForElement:(MPElementEntity *)element - fromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath; -+ (instancetype)dequeueCellForTransientSite:(NSString *)siteName - fromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath; - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m b/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m deleted file mode 100644 index c67192d0..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m +++ /dev/null @@ -1,411 +0,0 @@ -/** -* 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 -*/ - -// -// MPPasswordTypesCell.h -// MPPasswordTypesCell -// -// Created by lhunath on 2014-03-27. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import "MPPasswordTypesCell.h" -#import "MPPasswordLargeCell.h" -#import "MPiOSAppDelegate.h" -#import "MPAppDelegate_Store.h" -#import "MPPasswordLargeDeleteCell.h" - -@implementation MPPasswordTypesCell { - NSManagedObjectID *_elementOID; - BOOL _scrolling; -} - -#pragma mark - Lifecycle - -+ (instancetype)dequeueCellForTransientSite:(NSString *)siteName fromCollectionView:(UICollectionView *)collectionView - atIndexPath:(NSIndexPath *)indexPath { - - MPPasswordTypesCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass( [MPPasswordTypesCell class] ) - forIndexPath:indexPath]; - [cell setTransientSite:siteName]; - - return cell; -} - -+ (instancetype)dequeueCellForElement:(MPElementEntity *)element fromCollectionView:(UICollectionView *)collectionView - atIndexPath:(NSIndexPath *)indexPath { - - MPPasswordTypesCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass( [MPPasswordTypesCell class] ) - forIndexPath:indexPath]; - [cell setElement:element]; - - return cell; -} - -- (void)awakeFromNib { - - [super awakeFromNib]; - - self.backgroundColor = [UIColor clearColor]; - self.layer.shadowColor = [UIColor clearColor].CGColor; - - [self prepareForReuse]; -} - -- (void)prepareForReuse { - - _elementOID = nil; - _transientSite = nil; - _activeType = 0; - _algorithm = MPAlgorithmDefault; - - [super prepareForReuse]; -} - -- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { - - [super applyLayoutAttributes:layoutAttributes]; - - [self.contentCollectionView.collectionViewLayout invalidateLayout]; - [self scrollToActiveType]; -} - -#pragma mark - UICollectionViewDataSource - -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout - sizeForItemAtIndexPath:(NSIndexPath *)indexPath { - - return collectionView.bounds.size; -} - -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - - if (!self.algorithm) - return 0; - - if (self.transientSite) - return [[self.algorithm allTypes] count]; - - return [[self.algorithm allTypes] count] + 1 /* Delete */; -} - -- (MPPasswordLargeCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - MPPasswordLargeCell *cell; - if (!self.transientSite && indexPath.item == 0) { - cell = [MPPasswordLargeDeleteCell dequeueCellFromCollectionView:collectionView atIndexPath:indexPath]; - [cell updateWithElement:self.mainElement]; - } - else { - cell = [MPPasswordLargeCell dequeueCellWithType:[self typeForContentIndexPath:indexPath] fromCollectionView:collectionView - atIndexPath:indexPath]; - - [cell prepareForReuse]; - - if (self.transientSite) - [cell updateWithTransientSite:self.transientSite]; - else - [cell updateWithElement:self.mainElement]; - } - - if (_scrolling) - [cell willBeginDragging]; - - return cell; -} - -#pragma mark - UICollectionViewDelegateFlowLayout - -- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - - NSString *newSiteName = self.transientSite; - if (newSiteName) { - [[UIResponder findFirstResponder] resignFirstResponder]; - [PearlAlert showAlertWithTitle:@"Create Site" - message:strf( @"Do you want to create a new site named:\n%@", newSiteName ) - viewStyle:UIAlertViewStyleDefault - initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == [alert cancelButtonIndex]) { - // Cancel - for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems]) - [collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; - return; - } - - // Create - [[MPiOSAppDelegate get] addElementNamed:newSiteName completion:^(MPElementEntity *element) { - [self copyContentOfElement:element inCell:nil]; - PearlMainQueue( ^{ - [self.passwordsViewController updatePasswords]; - } ); - }]; - } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; - return; - } - - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - BOOL used = NO; - MPElementEntity *element = [self elementInContext:context]; - MPPasswordLargeCell *cell = (MPPasswordLargeCell *)[self.contentCollectionView cellForItemAtIndexPath:indexPath]; - if (!element) - wrn( @"No element to use for: %@", self ); - else if (indexPath.item == 0) { - [context deleteObject:element]; - [context saveToStore]; - } - else - used = [self copyContentOfElement:element inCell:cell]; - - PearlMainQueueAfter( 0.2f, ^{ - for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems]) - [collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES]; - - if (used) - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context_) { - [[self elementInContext:context_] use]; - [context_ saveToStore]; - }]; - } ); - }]; -} - -- (BOOL)copyContentOfElement:(MPElementEntity *)element inCell:(MPPasswordLargeCell *)cell { - - NSString *used, *pasteboardContent; - switch (cell.contentFieldMode) { - case MPContentFieldModePassword: - inf( @"Copying password for: %@", element.name ); - used = strl( @"Password" ); - pasteboardContent = [element resolveContentUsingKey:[MPAppDelegate_Shared get].key]; - break; - case MPContentFieldModeUser: - inf( @"Copying login for: %@", element.name ); - used = strl( @"Login" ); - pasteboardContent = element.loginName; - break; - } - - if ([pasteboardContent length]) { - [UIPasteboard generalPasteboard].string = pasteboardContent; - [PearlOverlay showTemporaryOverlayWithTitle:strl(@"%@ Copied", used) dismissAfter:2]; - return YES; - } - - return NO; -} - -#pragma mark - UIScrollViewDelegate - -- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - _scrolling = YES; - for (MPPasswordLargeCell *cell in [self.contentCollectionView visibleCells]) - [cell willBeginDragging]; -} - -- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity - targetContentOffset:(inout CGPoint *)targetContentOffset { - - if (scrollView == self.contentCollectionView) { - NSIndexPath *targetIndexPath = [self.contentCollectionView indexPathForItemAtPoint: - CGPointPlusCGPoint( *targetContentOffset, self.contentCollectionView.center )]; - *targetContentOffset = CGRectGetTopLeft( - [self.contentCollectionView layoutAttributesForItemAtIndexPath:targetIndexPath].frame ); - } -} - -- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { - - _scrolling = NO; - if (scrollView == self.contentCollectionView && !decelerate) - [self saveContentType]; -} - -- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - _scrolling = NO; - if (scrollView == self.contentCollectionView) - [self saveContentType]; -} - -#pragma mark - Private - -- (void)reloadData { - - if (self.transientSite) - PearlMainQueue( ^{ - self.algorithm = MPAlgorithmDefault; - self.activeType = [[MPiOSAppDelegate get] activeUserForMainThread].defaultType?: MPElementTypeGeneratedLong; - - for (NSInteger section = 0; section < [self.contentCollectionView numberOfSections]; ++section) - for (NSInteger item = 0; item < [self.contentCollectionView numberOfItemsInSection:section]; ++item) - [(MPPasswordLargeCell *)[self.contentCollectionView cellForItemAtIndexPath: - [NSIndexPath indexPathForItem:item inSection:section]] updateWithTransientSite:self.transientSite]; - } ); - else - [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { - MPElementEntity *mainElement = self.transientSite? nil: [self elementInContext:mainContext]; - - self.algorithm = mainElement.algorithm?: MPAlgorithmDefault; - self.activeType = mainElement.type?: [[MPiOSAppDelegate get] activeUserInContext:mainContext].defaultType?: - MPElementTypeGeneratedLong; - - for (NSInteger section = 0; section < [self.contentCollectionView numberOfSections]; ++section) - for (NSInteger item = 0; item < [self.contentCollectionView numberOfItemsInSection:section]; ++item) { - MPPasswordLargeCell *cell = (MPPasswordLargeCell *)[self.contentCollectionView cellForItemAtIndexPath: - [NSIndexPath indexPathForItem:item inSection:section]]; - [self reloadData:cell withElement:mainElement]; - } - }]; -} - -- (void)reloadData:(MPPasswordLargeCell *)cell { - - [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { - [self reloadData:cell withElement:[self elementInContext:mainContext]]; - }]; -} - -- (void)reloadData:(MPPasswordLargeCell *)cell withElement:(MPElementEntity *)element { - - if (element) - [cell updateWithElement:element]; - else - [cell updateWithTransientSite:self.transientSite]; -} - -- (void)scrollToActiveType { - - if (self.activeType && self.activeType != (MPElementType)NSNotFound) - [self.contentCollectionView scrollToItemAtIndexPath:[self contentIndexPathForType:self.activeType] - atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]; -} - -- (MPElementType)typeForContentIndexPath:(NSIndexPath *)indexPath { - - if (self.transientSite) - return [[self.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN][indexPath.item] unsignedIntegerValue]; - - if (indexPath.item == 0) - return (MPElementType)NSNotFound; - - return [[self.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN][indexPath.item - 1] unsignedIntegerValue]; -} - -- (NSIndexPath *)contentIndexPathForType:(MPElementType)type { - - NSArray *types = [self.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN]; - for (NSInteger t = 0; t < [types count]; ++t) - if ([types[t] unsignedIntegerValue] == type) { - if (self.transientSite) - return [NSIndexPath indexPathForItem:t inSection:0]; - else - return [NSIndexPath indexPathForItem:t + 1 inSection:0]; - } - - Throw( @"Unsupported type: %lud", (long)type ); -} - -- (void)saveContentType { - - CGPoint centerPoint = CGRectGetCenter( self.contentCollectionView.bounds ); - NSIndexPath *centerIndexPath = [self.contentCollectionView indexPathForItemAtPoint:centerPoint]; - MPElementType type = [self typeForContentIndexPath:centerIndexPath]; - if (type == ((MPElementType)NSNotFound)) - // Active cell is not a type cell. - return; - - self.activeType = type; - - if (self.transientSite) - return; - - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPPasswordLargeCell *cell = (MPPasswordLargeCell *)[self.contentCollectionView cellForItemAtIndexPath:centerIndexPath]; - if (!cell) { - err( @"Couldn't find cell to change type: centerIndexPath=%@", centerIndexPath ); - return; - } - - MPElementEntity *element = [self elementInContext:context]; - if (!element || element.type == cell.type) - // Nothing changed. - return; - - self.element = [cell saveContentTypeWithElement:element saveInContext:context]; - }]; -} - -#pragma mark - State - -- (void)setTransientSite:(NSString *)transientSite { - - if ([_transientSite isEqualToString:transientSite]) - return; - - dbg( @"transientSite: %@ -> %@", _transientSite, transientSite ); - - _transientSite = transientSite; - _elementOID = nil; - - [self updateAnimated:YES]; - [self reloadData]; -} - -- (void)setElement:(MPElementEntity *)element { - - NSManagedObjectID *newElementOID = element.objectID; - NSAssert( !newElementOID.isTemporaryID, @"Element doesn't have a permanent objectID: %@", element ); - if ([_elementOID isEqual:newElementOID]) - return; - - dbg( @"element: %@ -> %@", _elementOID, newElementOID ); - - _transientSite = nil; - _elementOID = newElementOID; - - [self updateAnimated:YES]; - [self reloadData]; -} - -- (MPElementEntity *)mainElement { - - return [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; -} - -- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context { - - return [MPElementEntity existingObjectWithID:_elementOID inContext:context]; -} - -- (void)setActiveType:(MPElementType)activeType { - - _activeType = activeType; - - [self scrollToActiveType]; -} - -- (void)setSelected:(BOOL)selected { - - [super setSelected:selected]; - - if (!selected) - for (NSIndexPath *indexPath in [self.contentCollectionView indexPathsForSelectedItems]) - [self.contentCollectionView deselectItemAtIndexPath:indexPath animated:YES]; -} - -- (void)setAlgorithm:(id)algorithm { - - _algorithm = algorithm; - - [self.contentCollectionView reloadData]; -} - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordsCoachmarkViewController.h b/MasterPassword/ObjC/iOS/MPPasswordsCoachmarkViewController.h deleted file mode 100644 index 4d224383..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordsCoachmarkViewController.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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 - */ - -// -// MPPasswordsCoachmarkViewController.h -// MPPasswordsCoachmarkViewController -// -// Created by lhunath on 2014-04-23. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import -#import "MPCoachmarkViewController.h" - -@interface MPPasswordsCoachmarkViewController : MPCoachmarkViewController -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordsCoachmarkViewController.m b/MasterPassword/ObjC/iOS/MPPasswordsCoachmarkViewController.m deleted file mode 100644 index 1198589a..00000000 --- a/MasterPassword/ObjC/iOS/MPPasswordsCoachmarkViewController.m +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 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 - */ - -// -// MPPasswordsCoachmarkViewController.h -// MPPasswordsCoachmarkViewController -// -// Created by lhunath on 2014-04-23. -// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved. -// - -#import "MPPasswordsCoachmarkViewController.h" -#import "MPPasswordLargeGeneratedCell.h" -#import "MPPasswordLargeStoredCell.h" - -@implementation MPPasswordsCoachmarkViewController - -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - - return 2; -} - -- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - if (indexPath.item == 0) { - MPPasswordLargeGeneratedCell *cell = [MPPasswordLargeGeneratedCell dequeueCellWithType:MPElementTypeGeneratedLong - fromCollectionView:collectionView atIndexPath:indexPath]; - [cell updateWithTransientSite:@"apple.com"]; - - return cell; - } - else if (indexPath.item == 1) { - MPPasswordLargeStoredCell *cell = [MPPasswordLargeStoredCell dequeueCellWithType:MPElementTypeStoredPersonal - fromCollectionView:collectionView atIndexPath:indexPath]; - [cell updateWithTransientSite:@"gmail.com"]; - [cell.contentField setText:@"PaS$w0rD"]; - - return cell; - } - - Throw(@"Unexpected item for indexPath: %@", indexPath); -} - -@end diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m index ccc4c06d..c516ed85 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m +++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m @@ -1,12 +1,12 @@ /** - * 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 - */ +* 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 +*/ // // MPPasswordsViewController.h @@ -19,11 +19,9 @@ #import "MPPasswordsViewController.h" #import "MPiOSAppDelegate.h" #import "MPAppDelegate_Store.h" -#import "MPPasswordLargeCell.h" -#import "MPPasswordTypesCell.h" #import "MPPopdownSegue.h" #import "MPAppDelegate_Key.h" -#import "MPCoachmarkViewController.h" +#import "MPPasswordCell.h" @interface MPPasswordsViewController() @@ -58,7 +56,7 @@ [self.passwordsSearchBar enumerateViews:^(UIView *subview, BOOL *stop, BOOL *recurse) { if ([subview isKindOfClass:[UITextField class]]) ((UITextField *)subview).keyboardAppearance = UIKeyboardAppearanceDark; - } recurse:YES]; + } recurse:YES]; } - (void)viewWillAppear:(BOOL)animated { @@ -103,54 +101,34 @@ referenceSizeForHeaderInSection:(NSInteger)section { - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { - if (collectionView == self.passwordCollectionView) { - UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout; - CGFloat itemWidth = UIEdgeInsetsInsetRect(self.passwordCollectionView.bounds, layout.sectionInset).size.width; - return CGSizeMake( itemWidth, 100 ); - } - - Throw(@"Unexpected collection view: %@", collectionView); + UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout; + CGFloat itemWidth = UIEdgeInsetsInsetRect( self.passwordCollectionView.bounds, layout.sectionInset ).size.width; + return CGSizeMake( itemWidth, 100 ); } #pragma mark - UICollectionViewDataSource - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - if (collectionView == self.passwordCollectionView) - return [self.fetchedResultsController.sections count]; - - Throw(@"Unexpected collection view: %@", collectionView); + return [self.fetchedResultsController.sections count]; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - if (collectionView == self.passwordCollectionView) - return ![MPiOSAppDelegate get].activeUserOID? 0: - ((id)self.fetchedResultsController.sections[section]).numberOfObjects + - (!_exactMatch && [[self query] length]? 1: 0); - - Throw(@"Unexpected collection view: %@", collectionView); + return ![MPiOSAppDelegate get].activeUserOID? 0: + ((id)self.fetchedResultsController.sections[section]).numberOfObjects + + (!_exactMatch && [[self query] length]? 1: 0); } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - if (collectionView == self.passwordCollectionView) { - [UIView setAnimationsEnabled:NO]; - MPPasswordTypesCell *cell; - if (indexPath.item < ((id)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) { - MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath]; - cell = [MPPasswordTypesCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath]; - } - else - // New Site. - cell = [MPPasswordTypesCell dequeueCellForTransientSite:self.query fromCollectionView:collectionView atIndexPath:indexPath]; - cell.passwordsViewController = self; + MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath]; + if (indexPath.item < ((id)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) + [cell setElement:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO]; + else + [cell setTransientSite:self.query animated:NO]; - [UIView setAnimationsEnabled:YES]; - return cell; - } - - Throw(@"Unexpected collection view: %@", collectionView); + return cell; } - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind @@ -159,15 +137,35 @@ referenceSizeForHeaderInSection:(NSInteger)section { return [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"MPPasswordHeader" forIndexPath:indexPath]; } +#pragma mark - UIScrollDelegate + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + + if (scrollView == self.passwordCollectionView) + for (MPPasswordCell *cell in [self.passwordCollectionView visibleCells]) + [cell setMode:MPPasswordCellModePassword animated:YES]; +} + #pragma mark - NSFetchedResultsControllerDelegate - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { if (controller == _fetchedResultsController) { - [self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section]]; - if (![newIndexPath isEqual:indexPath]) - [self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section]]; + switch (type) { + case NSFetchedResultsChangeInsert: + [self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]]; + break; + case NSFetchedResultsChangeDelete: + [self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]]; + break; + case NSFetchedResultsChangeMove: + [self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; + break; + case NSFetchedResultsChangeUpdate: + [self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]]; + break; + } } } @@ -229,7 +227,6 @@ referenceSizeForHeaderInSection:(NSInteger)section { [self updatePasswords]; } - #pragma mark - Private - (void)registerObservers { @@ -237,19 +234,19 @@ referenceSizeForHeaderInSection:(NSInteger)section { if ([_notificationObservers count]) return; - Weakify(self); + Weakify( self ); _notificationObservers = @[ [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - Strongify(self); + Strongify( self ); self.passwordSelectionContainer.alpha = 0; }], [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil queue:nil usingBlock:^(NSNotification *note) { - Strongify(self); + Strongify( self ); _fetchedResultsController = nil; self.passwordsSearchBar.text = nil; @@ -258,7 +255,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - Strongify(self); + Strongify( self ); [self updatePasswords]; [UIView animateWithDuration:1 animations:^{ @@ -282,7 +279,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { - (void)observeStore { - Weakify(self); + Weakify( self ); NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady]; if (!_mocObserver && mainContext) @@ -296,7 +293,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { _storeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - Strongify(self); + Strongify( self ); _fetchedResultsController = nil; [self updatePasswords]; }]; @@ -312,9 +309,9 @@ referenceSizeForHeaderInSection:(NSInteger)section { - (void)updateConfigKey:(NSString *)key { - if (!key || [key isEqualToString:NSStringFromSelector( @selector(dictationSearch) )]) + if (!key || [key isEqualToString:NSStringFromSelector( @selector( dictationSearch ) )]) self.passwordsSearchBar.keyboardType = [[MPiOSConfig get].dictationSearch boolValue]? UIKeyboardTypeDefault: UIKeyboardTypeURL; - if (!key || [key isEqualToString:NSStringFromSelector( @selector(hidePasswords) )]) + if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )]) [self updatePasswords]; } @@ -338,7 +335,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { [NSPredicate predicateWithFormat:@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query]: [NSPredicate predicateWithFormat:@"user == %@", activeUserOID]; if (![self.fetchedResultsController performFetch:&error]) - err(@"Couldn't fetch elements: %@", error); + err( @"Couldn't fetch elements: %@", error ); _exactMatch = NO; for (MPElementEntity *entity in self.fetchedResultsController.fetchedObjects) @@ -351,17 +348,17 @@ referenceSizeForHeaderInSection:(NSInteger)section { [self.passwordCollectionView performBatchUpdates:^{ NSInteger fromSections = self.passwordCollectionView.numberOfSections; NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView]; - for (int section = 0; section < MAX(toSections, fromSections); section++) { + for (int section = 0; section < MAX( toSections, fromSections ); section++) { if (section >= fromSections) { - dbg(@"insertSections:%d", section); + dbg( @"insertSections:%d", section ); [self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]]; } else if (section >= toSections) { - dbg(@"deleteSections:%d", section); + dbg( @"deleteSections:%d", section ); [self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]]; } else { - dbg(@"reloadSections:%d", section); + dbg( @"reloadSections:%d", section ); [self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]]; } } @@ -387,7 +384,7 @@ referenceSizeForHeaderInSection:(NSInteger)section { [MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )]; fetchRequest.sortDescriptors = @[ - [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO] + [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO] ]; fetchRequest.fetchBatchSize = 10; _fetchedResultsController = [[NSFetchedResultsController alloc] @@ -405,13 +402,13 @@ referenceSizeForHeaderInSection:(NSInteger)section { [self setActive:active animated:NO completion:nil]; } -- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void (^)(BOOL finished))completion { +- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void ( ^ )(BOOL finished))completion { _active = active; [UIView animateWithDuration:animated? 0.4f: 0 animations:^{ - [self.navigationBarToTopConstraint layoutWithPriority:active? 1: UILayoutPriorityDefaultHigh]; - [self.passwordsToBottomConstraint layoutWithPriority:active? 1: UILayoutPriorityDefaultHigh]; + [[self.navigationBarToTopConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh] layoutIfNeeded]; + [[self.passwordsToBottomConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh] layoutIfNeeded]; } completion:completion]; } diff --git a/MasterPassword/ObjC/iOS/MPPopdownSegue.m b/MasterPassword/ObjC/iOS/MPPopdownSegue.m index 8856f1ad..73acef12 100644 --- a/MasterPassword/ObjC/iOS/MPPopdownSegue.m +++ b/MasterPassword/ObjC/iOS/MPPopdownSegue.m @@ -38,7 +38,7 @@ metrics:nil views:NSDictionaryOfVariableBindings(popdownView)]; [UIView animateWithDuration:0.3f animations:^{ - [passwordsVC.popdownToTopConstraint layoutWithPriority:1]; + [[passwordsVC.popdownToTopConstraint updatePriority:1] layoutIfNeeded]; } completion:^(BOOL finished) { [popdownVC didMoveToParentViewController:passwordsVC]; }]; @@ -49,7 +49,7 @@ [popdownVC willMoveToParentViewController:nil]; [UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionOverrideInheritedDuration animations:^{ - [passwordsVC.popdownToTopConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; + [[passwordsVC.popdownToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; } completion:^(BOOL finished) { [popdownVC.view removeFromSuperview]; [popdownVC removeFromParentViewController]; diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.m b/MasterPassword/ObjC/iOS/MPUsersViewController.m index 2d486a18..14eff51f 100644 --- a/MasterPassword/ObjC/iOS/MPUsersViewController.m +++ b/MasterPassword/ObjC/iOS/MPUsersViewController.m @@ -786,7 +786,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { // Manage the entry container depending on whether a user is activate or not. switch (activeUserState) { case MPActiveUserStateNone: { - self.navigationBarToTopConstraint.priority = UILayoutPriorityDefaultHigh; + [[self.navigationBarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; self.avatarCollectionView.scrollEnabled = YES; self.entryContainer.alpha = 0; self.footerContainer.alpha = 1; @@ -796,7 +796,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { case MPActiveUserStateUserName: case MPActiveUserStateMasterPasswordChoice: case MPActiveUserStateMasterPasswordConfirmation: { - [self.navigationBarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultHigh]; + [[self.navigationBarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded]; self.avatarCollectionView.scrollEnabled = NO; self.entryContainer.alpha = 1; self.footerContainer.alpha = 1; @@ -804,7 +804,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) { break; } case MPActiveUserStateMinimized: { - [self.navigationBarToTopConstraint layoutWithPriority:1]; + [[self.navigationBarToTopConstraint updatePriority:1] layoutIfNeeded]; self.avatarCollectionView.scrollEnabled = NO; self.entryContainer.alpha = 0; self.footerContainer.alpha = 0; diff --git a/MasterPassword/ObjC/iOS/MPWebViewController.m b/MasterPassword/ObjC/iOS/MPWebViewController.m index 94ccc45c..98c9bcc1 100644 --- a/MasterPassword/ObjC/iOS/MPWebViewController.m +++ b/MasterPassword/ObjC/iOS/MPWebViewController.m @@ -24,7 +24,7 @@ [super viewDidLoad]; - [self.view adjustContentInsets]; + [self.webView.scrollView insetOcclusion]; } - (void)viewWillAppear:(BOOL)animated { diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj index 7b9ac689..672b51c1 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 93D391C07818F4C2DC1B6956 /* MPPasswordsCoachmarkViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D397E3650384498E7E53C4 /* MPPasswordsCoachmarkViewController.m */; }; 93D391ECBD9BD2C64115B5DD /* PearlSizedTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */; }; 93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */; }; 93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; }; @@ -19,7 +18,6 @@ 93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; }; 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.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 */; }; @@ -31,10 +29,8 @@ 93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */; }; 93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */; }; 93D3980046016EFD05B35BC5 /* PearlUICollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */; }; - 93D398BD8B83FEE8BE4EFFFC /* MPPasswordLargeDeleteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39EFBC4D6BA3C8581865F /* MPPasswordLargeDeleteCell.m */; }; 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 */; }; 93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D391943675426839501BB8 /* MPLogsViewController.h */; }; @@ -45,14 +41,12 @@ 93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; }; 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; - 93D39CB5E2EC1078E898F46A /* MPPasswordLargeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937863061C3916AF7AD2 /* MPPasswordLargeCell.m */; }; 93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; }; 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; 93D39E34FD28D24FE3442C48 /* UITextView+PearlAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3977321EB249981821AB0 /* UITextView+PearlAttributes.m */; }; 93D39EAA4D064193074D3021 /* MPFixable.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A813CA9D7E192261ED2 /* MPFixable.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 */; }; DA071BF3190187FE00179766 /* empty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA071BF1190187FE00179766 /* empty@2x.png */; }; DA071BF4190187FE00179766 /* empty.png in Resources */ = {isa = PBXBuildFile; fileRef = DA071BF2190187FE00179766 /* empty.png */; }; @@ -85,6 +79,8 @@ DA250A18195665A100AC23F1 /* UITableView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */; }; DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */; }; DA250A1A195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */; }; + DA25C5F8197AFFB40046CDCF /* icon_tools.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD384C1711E29700CF925C /* icon_tools.png */; }; + DA25C5F9197AFFB40046CDCF /* icon_tools@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD384D1711E29700CF925C /* icon_tools@2x.png */; }; DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; }; DA2CA4DE18D28859007798F8 /* NSArray+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4DA18D28859007798F8 /* NSArray+Pearl.h */; }; DA2CA4DF18D28859007798F8 /* NSTimer+PearlBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4DB18D28859007798F8 /* NSTimer+PearlBlock.m */; }; @@ -347,10 +343,10 @@ DAEB938218AA537D000490CC /* x509_vfy.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEB933118AA537D000490CC /* x509_vfy.h */; }; DAEB938318AA537D000490CC /* x509v3.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEB933218AA537D000490CC /* x509v3.h */; }; DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; }; - DAEC85B518E3DD9A007FC0DF /* PearlUIView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B118E3DD9A007FC0DF /* PearlUIView.m */; }; + DAEC85B518E3DD9A007FC0DF /* UIView+Touches.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */; }; DAEC85B618E3DD9A007FC0DF /* PearlUINavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */; }; DAEC85B718E3DD9A007FC0DF /* PearlUINavigationBar.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEC85B318E3DD9A007FC0DF /* PearlUINavigationBar.h */; }; - DAEC85B818E3DD9A007FC0DF /* PearlUIView.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEC85B418E3DD9A007FC0DF /* PearlUIView.h */; }; + DAEC85B818E3DD9A007FC0DF /* UIView+Touches.h in Headers */ = {isa = PBXBuildFile; fileRef = DAEC85B418E3DD9A007FC0DF /* UIView+Touches.h */; }; DAF4EF50190A81E400023C90 /* NSManagedObject+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF4EF4E190A81E400023C90 /* NSManagedObject+Pearl.m */; }; DAF4EF51190A81E400023C90 /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF4EF4F190A81E400023C90 /* NSManagedObject+Pearl.h */; }; DAFC5656172C573B00CB5CC5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; @@ -455,11 +451,8 @@ /* Begin PBXFileReference section */ 93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = ""; }; 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; }; - 93D39097C0AAE62C1C321BFC /* MPPasswordTypesCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordTypesCell.m; sourceTree = ""; }; 93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = ""; }; 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlAdjustInsets.m"; sourceTree = ""; }; - 93D390FD93EFCFECB5193DEF /* MPPasswordsCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsCoachmarkViewController.h; sourceTree = ""; }; - 93D391243F64A77798B4D6A4 /* MPPasswordTypesCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordTypesCell.h; sourceTree = ""; }; 93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsViewController.h; sourceTree = ""; }; 93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlSizedTextView.m; sourceTree = ""; }; 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = ""; }; @@ -468,27 +461,21 @@ 93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = ""; }; 93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.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 = ""; }; 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 = ""; }; - 93D3966B527CA47A0A661CE2 /* MPPasswordLargeDeleteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeDeleteCell.h; sourceTree = ""; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = ""; }; 93D3970502644794E8A027BE /* MPNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNavigationController.h; sourceTree = ""; }; 93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = ""; }; 93D39730673227EFF6DEFF19 /* MPSetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSetupViewController.h; sourceTree = ""; }; 93D3977321EB249981821AB0 /* UITextView+PearlAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+PearlAttributes.m"; sourceTree = ""; }; 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLogsViewController.m; sourceTree = ""; }; - 93D397E3650384498E7E53C4 /* MPPasswordsCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsCoachmarkViewController.m; sourceTree = ""; }; 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = ""; }; 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = ""; }; 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = ""; }; - 93D3993422E207BF0B21D089 /* MPPasswordLargeGeneratedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeGeneratedCell.m; sourceTree = ""; }; 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = ""; }; 93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = ""; }; 93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = ""; }; @@ -510,15 +497,12 @@ 93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = ""; }; 93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.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 = ""; }; 93D39DA27D768B53C8B1330C /* MPAvatarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAvatarCell.h; sourceTree = ""; }; 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 = ""; }; - 93D39EFBC4D6BA3C8581865F /* MPPasswordLargeDeleteCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeDeleteCell.m; sourceTree = ""; }; 93D39F556F2F142740A65E59 /* MPWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPWebViewController.h; 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 = ""; }; @@ -1380,10 +1364,10 @@ DAEB933118AA537D000490CC /* x509_vfy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x509_vfy.h; sourceTree = ""; }; DAEB933218AA537D000490CC /* x509v3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x509v3.h; sourceTree = ""; }; DAEBC45214F6364500987BF6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; - DAEC85B118E3DD9A007FC0DF /* PearlUIView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUIView.m; sourceTree = ""; }; + DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Touches.m"; sourceTree = ""; }; DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUINavigationBar.m; sourceTree = ""; }; DAEC85B318E3DD9A007FC0DF /* PearlUINavigationBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUINavigationBar.h; sourceTree = ""; }; - DAEC85B418E3DD9A007FC0DF /* PearlUIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUIView.h; sourceTree = ""; }; + DAEC85B418E3DD9A007FC0DF /* UIView+Touches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Touches.h"; sourceTree = ""; }; DAF4EF4E190A81E400023C90 /* NSManagedObject+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+Pearl.m"; sourceTree = ""; }; DAF4EF4F190A81E400023C90 /* NSManagedObject+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+Pearl.h"; sourceTree = ""; }; DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libInAppSettingsKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1614,8 +1598,6 @@ DACA22121705DDC5002C6C22 /* External */, DA5BFA47147E415C00F98B1E /* Frameworks */, DA5BFA45147E415C00F98B1E /* Products */, - 93D3966B527CA47A0A661CE2 /* MPPasswordLargeDeleteCell.h */, - 93D39EFBC4D6BA3C8581865F /* MPPasswordLargeDeleteCell.m */, ); sourceTree = ""; }; @@ -2441,6 +2423,7 @@ DABD3BD71711E2DC00CF925C /* iOS */ = { isa = PBXGroup; children = ( + DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */, 93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */, 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */, 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */, @@ -2469,21 +2452,12 @@ 93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */, 93D393310223DDB35218467A /* MPCombinedViewController.m */, 93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */, - DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */, 93D39B381350802A194BF332 /* MPAvatarCell.m */, 93D39DA27D768B53C8B1330C /* MPAvatarCell.h */, - 93D3993422E207BF0B21D089 /* MPPasswordLargeGeneratedCell.m */, - 93D39CE1138FDA4D3D1B847A /* MPPasswordLargeGeneratedCell.h */, 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */, 93D3971FE104BB4052484151 /* MPUsersViewController.h */, - 93D39E02F69CACAB61C056F8 /* MPPasswordLargeCell.h */, - 93D3937863061C3916AF7AD2 /* MPPasswordLargeCell.m */, - 93D395BA6B2CFF5F49A4D25F /* MPPasswordLargeStoredCell.h */, - 93D3947F6BB69CA9A9124A5D /* MPPasswordLargeStoredCell.m */, 93D39BAA71DE51B4D8A1286C /* MPCell.m */, 93D390519405B76CC6A57C4F /* MPCell.h */, - 93D39097C0AAE62C1C321BFC /* MPPasswordTypesCell.m */, - 93D391243F64A77798B4D6A4 /* MPPasswordTypesCell.h */, 93D3937712BF1B67623E5764 /* MPEmergencySegue.m */, 93D39A41340CF778E00D0E6D /* MPEmergencySegue.h */, 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */, @@ -2494,8 +2468,6 @@ 93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */, 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */, 93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */, - 93D397E3650384498E7E53C4 /* MPPasswordsCoachmarkViewController.m */, - 93D390FD93EFCFECB5193DEF /* MPPasswordsCoachmarkViewController.h */, 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */, 93D39F556F2F142740A65E59 /* MPWebViewController.h */, 93D39CC01630D0421205C4C4 /* MPNavigationController.m */, @@ -2846,10 +2818,10 @@ DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */, DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */, DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */, - DAEC85B118E3DD9A007FC0DF /* PearlUIView.m */, + DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */, DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */, DAEC85B318E3DD9A007FC0DF /* PearlUINavigationBar.h */, - DAEC85B418E3DD9A007FC0DF /* PearlUIView.h */, + DAEC85B418E3DD9A007FC0DF /* UIView+Touches.h */, DA2CA4E518D2AC10007798F8 /* NSLayoutConstraint+PearlUIKit.m */, DA2CA4E218D28866007798F8 /* NSLayoutConstraint+PearlUIKit.h */, 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */, @@ -3016,7 +2988,7 @@ DAEB933C18AA537D000490CC /* aes.h in Headers */, DAEB937618AA537D000490CC /* ssl2.h in Headers */, DAFE4A3C15039824003ABA7C /* Pearl-UIKit-Dependencies.h in Headers */, - DAEC85B818E3DD9A007FC0DF /* PearlUIView.h in Headers */, + DAEC85B818E3DD9A007FC0DF /* UIView+Touches.h in Headers */, DAEB935B18AA537D000490CC /* krb5_asn.h in Headers */, DAEB935818AA537D000490CC /* evp.h in Headers */, DAEB934118AA537D000490CC /* blowfish.h in Headers */, @@ -3452,6 +3424,7 @@ DABD3B8D1711E29800CF925C /* keypad.png in Resources */, DABD3B8E1711E29800CF925C /* logo-bare.png in Resources */, DA7304E6194E025900E72520 /* tip_basic_black@2x.png in Resources */, + DA25C5F9197AFFB40046CDCF /* icon_tools@2x.png in Resources */, DABD3B8F1711E29800CF925C /* menu-icon.png in Resources */, DABD3B901711E29800CF925C /* menu-icon@2x.png in Resources */, DABD3B951711E29800CF925C /* pull-down.png in Resources */, @@ -3466,6 +3439,7 @@ DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */, DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */, DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */, + DA25C5F8197AFFB40046CDCF /* icon_tools.png in Resources */, DA2509FB1956484D00AC23F1 /* image-11@2x.png in Resources */, DA250A0B1956484D00AC23F1 /* image-3@2x.png in Resources */, DABD3FCA1712446200CF925C /* cloud.png in Resources */, @@ -3560,11 +3534,7 @@ 93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */, 93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */, 93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */, - 93D39CB5E2EC1078E898F46A /* MPPasswordLargeCell.m in Sources */, - 93D39FA97F4C3F69A75D5A03 /* MPPasswordLargeGeneratedCell.m in Sources */, - 93D394F6D3F6E2553AA0D684 /* MPPasswordLargeStoredCell.m in Sources */, 93D39392DEDA376F93C6C718 /* MPCell.m in Sources */, - 93D399278165FD6D950F0025 /* MPPasswordTypesCell.m in Sources */, 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */, 93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */, 93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */, @@ -3572,10 +3542,8 @@ 93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */, 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */, 93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */, - 93D391C07818F4C2DC1B6956 /* MPPasswordsCoachmarkViewController.m in Sources */, 93D39EAA4D064193074D3021 /* MPFixable.m in Sources */, 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */, - 93D398BD8B83FEE8BE4EFFFC /* MPPasswordLargeDeleteCell.m in Sources */, 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3643,7 +3611,7 @@ DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */, DAFE4A63150399FF003ABA82 /* UIControl+PearlBlocks.m in Sources */, DAFE4A63150399FF003ABA86 /* NSObject+PearlKVO.m in Sources */, - DAEC85B518E3DD9A007FC0DF /* PearlUIView.m in Sources */, + DAEC85B518E3DD9A007FC0DF /* UIView+Touches.m in Sources */, DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */, DAFE4A63150399FF003ABA8A /* UIControl+PearlSelect.m in Sources */, DAFE4A63150399FF003ABA8E /* UIScrollView+PearlFlashingIndicators.m in Sources */, @@ -3931,7 +3899,7 @@ Reveal, ); PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = "1F49F193-0915-40B1-8792-BBDE74185E45"; + "PROVISIONING_PROFILE[sdk=iphoneos*]" = "94333D7F-68F7-473D-B3B1-86AA41F33449"; SKIP_INSTALL = NO; TARGETED_DEVICE_FAMILY = 1; }; diff --git a/MasterPassword/ObjC/iOS/Storyboard.storyboard b/MasterPassword/ObjC/iOS/Storyboard.storyboard index 0b692f56..b21ebb15 100644 --- a/MasterPassword/ObjC/iOS/Storyboard.storyboard +++ b/MasterPassword/ObjC/iOS/Storyboard.storyboard @@ -1,5 +1,5 @@ - + @@ -150,7 +150,7 @@ - + @@ -236,7 +236,7 @@ - + @@ -980,11 +980,11 @@ - + - + @@ -1004,371 +1004,322 @@ - + - + - - - - - - - - - - + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + @@ -1527,7 +1478,7 @@ - + @@ -2541,8 +2492,10 @@ See + +