2
0

Completed password cell handling and misc UI and moc update improvements.

[UPDATED]   Make private moc parent of all private blocks to avoid blocking the main thread for writes, update the main moc on private moc updates.
[FIXED]     Don't cause a crash on elements with a bad type.
[UPDATED]   Improved cell handling and UI update handling.
[UPDATED]   Replace FontReplacer with moarfonts to fix issues in UICollectionViewCells.
This commit is contained in:
Maarten Billemont 2014-04-06 23:34:18 -04:00
parent f475c15360
commit bd37f1d6a7
52 changed files with 2059 additions and 1357 deletions

@ -1 +0,0 @@
Subproject commit 4e3dea08702906fc5d51c8b75dda5da29c545a74

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit b50115c767e7c0d412099ae169891dbb625b8c64
Subproject commit d462ada876939cab875e08736b1efc7af065848d

View File

@ -31,6 +31,21 @@
return 0;
}
- (NSString *)description {
return strf( @"<%@: version=%d>", NSStringFromClass( [self class] ), self.version );
}
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other conformsToProtocol:@protocol(MPAlgorithm)])
return NO;
return [(id<MPAlgorithm>)other version] == [self version];
}
- (BOOL)migrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
NSError *error = nil;
@ -250,23 +265,24 @@
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]);
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,
[name dataUsingEncoding:NSUTF8StringEncoding],
counterBytes,
nil]
nameLengthBytes, [name dataUsingEncoding:NSUTF8StringEncoding],
counterBytes, nil]
hmacWith:PearlHashSHA256 key:key.keyData];
trc(@"seed is: %@", [seed encodeBase64]);
const char *seedBytes = seed.bytes;
// Determine the cipher from the first seed byte.
NSAssert([seed length], @"Missing seed.");
NSArray *typeCiphers = [[MPTypes_ciphers valueForKey:[self classNameOfType:type]]
valueForKey:[self nameOfType:type]];
NSString *typeClass = [self classNameOfType:type];
NSString *typeName = [self nameOfType:type];
id classCiphers = [MPTypes_ciphers valueForKey:typeClass];
NSArray *typeCiphers = [classCiphers valueForKey:typeName];
NSString *cipher = typeCiphers[htons(seedBytes[0]) % [typeCiphers count]];
trc(@"type %@, ciphers: %@, selected: %@", [self nameOfType:type], typeCiphers, cipher);
trc(@"type %@, ciphers: %@, selected: %@", typeName, 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.");
@ -275,8 +291,7 @@
uint16_t keyByte = htons(seedBytes[c + 1]);
NSString *cipherClass = [cipher substringWithRange:NSMakeRange( c, 1 )];
NSString *cipherClassCharacters = [[MPTypes_ciphers valueForKey:@"MPCharacterClasses"] valueForKey:cipherClass];
NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length],
1 )];
NSString *character = [cipherClassCharacters substringWithRange:NSMakeRange( keyByte % [cipherClassCharacters length], 1 )];
trc(@"class %@ has characters: %@, index: %u, selected: %@", cipherClass, cipherClassCharacters, keyByte, character);
[content appendString:character];

View File

@ -168,7 +168,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
MPKey *recoverKey = newKey;
#ifdef PEARL_UIKIT
PearlOverlay *activityOverlay = [PearlOverlay showOverlayWithTitle:PearlString( @"Migrating %ld sites...", (long)[user.elements count] )];
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:PearlString( @"Migrating %ld sites...", (long)[user.elements count] )];
#endif
for (MPElementEntity *element in user.elements) {

View File

@ -30,11 +30,12 @@
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc {
if (!self.activeUserOID || !moc)
NSManagedObjectID *activeUserOID = self.activeUserOID;
if (!activeUserOID || !moc)
return nil;
NSError *error;
MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:self.activeUserOID error:&error];
MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:activeUserOID error:&error];
if (!activeUser) {
[self signOutAnimated:YES];
err(@"Failed to retrieve active user: %@", error);

View File

@ -30,7 +30,7 @@ typedef enum {
/** @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;
- (MPElementEntity *)changeElement:(MPElementEntity *)element inContext:(NSManagedObjectContext *)context toType:(MPElementType)type;
- (MPElementEntity *)changeElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context toType:(MPElementType)type;
- (MPImportResult)importSites:(NSString *)importedSitesString
askImportPassword:(NSString *(^)(NSString *userName))importPassword
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;

View File

@ -32,7 +32,8 @@ typedef NS_ENUM(NSInteger, MPMigrationLevelCloudStore) {
};
@implementation MPAppDelegate_Shared(Store)
PearlAssociatedObjectProperty(NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext);
PearlAssociatedObjectProperty(id, SaveObserver, saveObserver);
PearlAssociatedObjectProperty(NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext);
PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext);
@ -50,7 +51,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock {
NSManagedObjectContext *mainManagedObjectContext = [self managedObjectContextForMainThreadIfReady];
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
if (!mainManagedObjectContext)
return NO;
@ -63,7 +64,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void (^)(NSManagedObjectContext *mainContext))mocBlock {
NSManagedObjectContext *mainManagedObjectContext = [self managedObjectContextForMainThreadIfReady];
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
if (!mainManagedObjectContext)
return NO;
@ -318,6 +319,11 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
[moc saveToStore];
[moc reset];
if (self.saveObserver) {
[[NSNotificationCenter defaultCenter] removeObserver:self.saveObserver];
self.saveObserver = nil;
}
self.privateManagedObjectContext = nil;
self.mainManagedObjectContext = nil;
}];
@ -334,8 +340,8 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
} );
// Create our contexts.
NSManagedObjectContext
*privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
NSManagedObjectContext *privateManagedObjectContext =
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[privateManagedObjectContext performBlockAndWait:^{
privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
privateManagedObjectContext.persistentStoreCoordinator = coordinator;
@ -358,6 +364,16 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
NSManagedObjectContext *mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
mainManagedObjectContext.parentContext = privateManagedObjectContext;
if (self.saveObserver)
[[NSNotificationCenter defaultCenter] removeObserver:self.saveObserver];
self.saveObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification object:privateManagedObjectContext queue:nil usingBlock:
^(NSNotification *note) {
[mainManagedObjectContext performBlock:^{
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
}];
}];
self.privateManagedObjectContext = privateManagedObjectContext;
self.mainManagedObjectContext = mainManagedObjectContext;
}
@ -386,12 +402,12 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [self activeUserInContext:context];
NSAssert(activeUser, @"Missing user.");
if (!activeUser)
if (!activeUser) {
completion( nil );
return;
}
MPElementType type = activeUser.defaultType;
if (!type)
type = activeUser.defaultType = MPElementTypeGeneratedLong;
NSString *typeEntityClassName = [MPAlgorithmDefault classNameOfType:type];
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:typeEntityClassName
@ -408,18 +424,19 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
if (element.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ element ] error:&error])
err(@"Failed to obtain a permanent object ID after creating new element: %@", error);
NSManagedObjectID *elementOID = [element objectID];
dispatch_async( dispatch_get_main_queue(), ^{
completion(
(MPElementEntity *)[[MPAppDelegate_Shared managedObjectContextForMainThreadIfReady] objectRegisteredForID:elementOID] );
} );
completion( element );
}];
}
- (MPElementEntity *)changeElement:(MPElementEntity *)element inContext:(NSManagedObjectContext *)context toType:(MPElementType)type {
- (MPElementEntity *)changeElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context toType:(MPElementType)type {
if ([element.algorithm classOfType:type] == element.typeClass)
if (element.type == type)
return element;
if ([element.algorithm classOfType:type] == element.typeClass) {
element.type = type;
[context saveToStore];
}
else {
// Type requires a different class of element. Recreate the element.
@ -435,14 +452,13 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
newElement.loginName = element.loginName;
[context deleteObject:element];
// TODO: Dodgy... we're not saving consistently here.
// Either we should save regardless and change the method signature to saveInContext: or not save at all.
[context saveToStore];
NSError *error;
if (![context obtainPermanentIDsForObjects:@[ newElement ] error:&error])
err(@"Failed to obtain a permanent object ID after changing object type: %@", error);
[[NSNotificationCenter defaultCenter] postNotificationName:MPElementUpdatedNotification object:element.objectID];
element = newElement;
}
@ -728,7 +744,7 @@ PearlAssociatedObjectProperty(NSManagedObjectContext*, MainManagedObjectContext,
[export appendFormat:@"%@ %8ld %8s %20s\t%@\n",
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses,
[PearlString( @"%lu:%lu", (long)type, (unsigned long)version ) UTF8String], [name UTF8String], content
? content: @""];
? content: @""];
}
MPCheckpoint( MPCheckpointSitesExported, @{

View File

@ -8,13 +8,14 @@
#import "MPEntities.h"
#import "MPAppDelegate_Shared.h"
#import "MPAppDelegate_Store.h"
@implementation NSManagedObjectContext(MP)
- (BOOL)saveToStore {
__block BOOL success = YES;
if ([self hasChanges])
if ([self hasChanges]) {
[self performBlockAndWait:^{
@try {
NSError *error = nil;
@ -26,6 +27,7 @@
err(@"While saving: %@", exception);
}
}];
}
return success && (!self.parentContext || [self.parentContext saveToStore]);
}
@ -42,6 +44,14 @@
type = [self.user defaultType];
if (!type || type == (MPElementType)NSNotFound)
type = MPElementTypeGeneratedLong;
if (![self isKindOfClass:[self.algorithm classOfType:type]]) {
// NSAssert(NO, @"This object's class does not support the type: %lu", (long)type);
for (MPElementType aType = type; type != (aType = [self.algorithm nextType:aType]);)
if ([self isKindOfClass:[self.algorithm classOfType:aType]]) {
err(@"Invalid type for: %@, type: %lu. Will use %lu instead.", self.name, (long)type, (long)aType);
return aType;
}
}
return type;
}
@ -199,7 +209,7 @@
- (MPElementType)defaultType {
return (MPElementType)[self.defaultType_ unsignedIntegerValue];
return IfElse((MPElementType)[self.defaultType_ unsignedIntegerValue], MPElementTypeGeneratedLong);
}
- (void)setDefaultType:(MPElementType)aDefaultType {

View File

@ -113,9 +113,8 @@
// "Next type" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
element = [[MPMacAppDelegate get] changeElement:element inContext:context
element = [[MPMacAppDelegate get] changeElement:element saveInContext:context
toType:[element.algorithm nextType:element.type]];
[context saveToStore];
self.representedObject = [[MPElementModel alloc] initWithEntity:element];
}];
@ -131,9 +130,8 @@
// "Previous type" button.
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [self.representedObject entityInContext:context];
element = [[MPMacAppDelegate get] changeElement:element inContext:context
element = [[MPMacAppDelegate get] changeElement:element saveInContext:context
toType:[element.algorithm previousType:element.type]];
[context saveToStore];
self.representedObject = [[MPElementModel alloc] initWithEntity:element];
}];

View File

@ -182,7 +182,7 @@
// "Create" button.
[[MPMacAppDelegate get] addElementNamed:[self.siteField stringValue] completion:^(MPElementEntity *element) {
if (element)
[self updateElements];
PearlMainQueue( ^{ [self updateElements]; } );
}];
break;
}

View File

@ -37,6 +37,7 @@ typedef NS_ENUM(NSUInteger, MPAvatarMode) {
@property (assign, nonatomic) MPAvatarMode mode;
@property (assign, nonatomic) CGFloat visibility;
@property (assign, nonatomic) BOOL spinnerActive;
@property (assign, nonatomic, readonly) BOOL newUser;
+ (NSString *)reuseIdentifier;

View File

@ -17,6 +17,7 @@
//
#import "MPAvatarCell.h"
#import "MPPasswordLargeCell.h"
const long MPAvatarAdd = 10000;
@ -34,6 +35,7 @@ const long MPAvatarAdd = 10000;
@end
@implementation MPAvatarCell {
CAAnimationGroup *_targetedShadowAnimation;
}
+ (NSString *)reuseIdentifier {
@ -47,6 +49,8 @@ const long MPAvatarAdd = 10000;
[super awakeFromNib];
self.alpha = 0;
self.nameContainer.layer.cornerRadius = 5;
self.avatarImageView.hidden = NO;
@ -73,15 +77,21 @@ const long MPAvatarAdd = 10000;
pulseShadowOpacityAnimation.autoreverses = YES;
pulseShadowOpacityAnimation.repeatCount = MAXFLOAT;
CAAnimationGroup *group = [CAAnimationGroup new];
group.animations = @[ toShadowOpacityAnimation, pulseShadowOpacityAnimation ];
group.duration = MAXFLOAT;
[self.avatarImageView.layer addAnimation:group forKey:@"targetedShadow"];
_targetedShadowAnimation = [CAAnimationGroup new];
_targetedShadowAnimation.animations = @[ toShadowOpacityAnimation, pulseShadowOpacityAnimation ];
_targetedShadowAnimation.duration = MAXFLOAT;
self.avatarImageView.layer.shadowColor = [UIColor whiteColor].CGColor;
self.avatarImageView.layer.shadowOffset = CGSizeZero;
}
- (void)prepareForReuse {
[super prepareForReuse];
_newUser = NO;
[self setVisibility:0 animated:NO];
[self setMode:MPAvatarModeLowered animated:NO];
[self setSpinnerActive:NO animated:NO];
}
- (void)dealloc {
@ -95,8 +105,11 @@ const long MPAvatarAdd = 10000;
_avatar = avatar;
if (avatar == MPAvatarAdd)
if (avatar == MPAvatarAdd) {
self.avatarImageView.image = [UIImage imageNamed:@"avatar-add"];
self.name = strl( @"New User" );
_newUser = YES;
}
else
self.avatarImageView.image = [UIImage imageNamed:strf( @"avatar-%ld", avatar )];
}
@ -151,6 +164,8 @@ const long MPAvatarAdd = 10000;
- (void)setSpinnerActive:(BOOL)spinnerActive animated:(BOOL)animated {
if (_spinnerActive == spinnerActive)
return;
_spinnerActive = spinnerActive;
CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
@ -180,10 +195,20 @@ const long MPAvatarAdd = 10000;
[UIView animateWithDuration:animated? 0.2f: 0 animations:^{
self.avatarImageView.transform = CGAffineTransformIdentity;
}];
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
[UIView animateWithDuration:animated? 0.3f: 0 delay:0 options:UIViewAnimationOptionOverrideInheritedDuration animations:^{
self.alpha = 1;
if (self.newUser) {
if (self.mode == MPAvatarModeLowered)
self.avatar = MPAvatarAdd;
else if (self.avatar == MPAvatarAdd)
self.avatar = arc4random() % MPAvatarCount;
}
switch (self.mode) {
case MPAvatarModeLowered: {
self.avatarSizeConstraint.constant = self.avatarImageView.image.size.height;
self.avatarRaisedConstraint.priority = UILayoutPriorityDefaultLow;
self.avatarToTopConstraint.priority = UILayoutPriorityDefaultLow;
@ -235,14 +260,20 @@ const long MPAvatarAdd = 10000;
self.nameContainer.alpha = 0;
self.nameContainer.backgroundColor = [UIColor blackColor];
self.avatarImageView.alpha = 1;
self.avatarImageView.layer.shadowOpacity = 0;
break;
}
}
[self.avatarSizeConstraint apply];
[self.avatarRaisedConstraint apply];
[self.avatarToTopConstraint apply];
[self.nameToCenterConstraint apply];
// Avatar minimized.
if (self.mode == MPAvatarModeRaisedAndMinimized)
[self.avatarImageView.layer removeAllAnimations];
else if (![self.avatarImageView.layer animationForKey:@"targetedShadow"])
[self.avatarImageView.layer addAnimation:_targetedShadowAnimation forKey:@"targetedShadow"];
// Avatar selection and spinner.
if (self.mode != MPAvatarModeRaisedAndMinimized && (self.selected || self.highlighted) && !self.spinnerActive)
self.avatarImageView.backgroundColor = self.avatarImageView.tintColor;
@ -250,7 +281,7 @@ const long MPAvatarAdd = 10000;
self.avatarImageView.backgroundColor = [UIColor clearColor];
self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
self.spinner.alpha = self.spinnerActive? 1: 0;
}];
} completion:nil];
}
@end

View File

@ -9,17 +9,15 @@
*/
//
// PearlUIView.h
// PearlUIView
// MPCell.h
// MPCell
//
// Created by lhunath on 2014-03-17.
// Created by lhunath on 2014-03-27.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface PearlUIView : UIView
@property(assign, nonatomic) BOOL ignoreTouches;
@interface MPCell : UICollectionViewCell
@end

View File

@ -9,18 +9,16 @@
*/
//
// PearlUINavigationBar.h
// PearlUINavigationBar
// MPCell.h
// MPCell
//
// Created by lhunath on 2014-03-17.
// Created by lhunath on 2014-03-27.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPCell.h"
@interface PearlUINavigationBar : UINavigationBar
@property (assign, nonatomic) BOOL ignoreTouches;
@property (assign, nonatomic) BOOL invisible;
@implementation MPCell {
}
@end

View File

@ -89,19 +89,18 @@
[self becomeFirstResponder];
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
switch (self.mode) {
case MPCombinedModeUserSelection: {
[self.usersVC setActive:YES animated:NO];
[self.passwordsVC setActive:NO animated:NO];
[self.usersVC setActive:YES animated:animated];
[self.passwordsVC setActive:NO animated:animated];
// MPUsersViewController *usersVC = [self.storyboard instantiateViewControllerWithIdentifier:@"MPUsersViewController"];
// [self setViewControllers:@[ usersVC ] direction:UIPageViewControllerNavigationDirectionReverse
// animated:animated completion:nil];
break;
}
case MPCombinedModePasswordSelection: {
[self.usersVC setActive:NO animated:NO];
[self.passwordsVC setActive:YES animated:NO];
[self.usersVC setActive:NO animated:animated];
[self.passwordsVC setActive:YES animated:animated];
// MPPasswordsViewController *passwordsVC = [self.storyboard instantiateViewControllerWithIdentifier:@"MPPasswordsViewController"];
// [self setViewControllers:@[ passwordsVC ] direction:UIPageViewControllerNavigationDirectionForward
// animated:animated completion:nil];
@ -110,7 +109,6 @@
}
[self.passwordsTopConstraint apply];
}];
}
#pragma mark - Private

View File

@ -66,10 +66,11 @@
__weak MPElementListAllViewController *wSelf = self;
[[MPiOSAppDelegate get] addElementNamed:[alert textFieldAtIndex:0].text completion:^(MPElementEntity *element) {
if (element) {
[wSelf.delegate didSelectElement:element];
[wSelf close:nil];
}
if (element)
PearlMainQueue( ^{
[wSelf.delegate didSelectElement:element];
[wSelf close:nil];
} );
}];
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonOkay, nil];
@ -85,7 +86,7 @@
if (buttonIndex == [alert cancelButtonIndex])
return;
PearlOverlay *activity = [PearlOverlay showOverlayWithTitle:@"Upgrading Sites"];
PearlOverlay *activity = [PearlOverlay showProgressOverlayWithTitle:@"Upgrading Sites"];
[self performUpgradeAllWithCompletion:^(BOOL success, NSDictionary *changes) {
dispatch_async( dispatch_get_main_queue(), ^{
[self showUpgradeChanges:changes];

View File

@ -193,7 +193,7 @@
NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
cell.textLabel.text = query;
cell.detailTextLabel.text = PearlString( @"New site: %@",
[MPAlgorithmDefault shortNameOfType:[[[MPiOSAppDelegate get] activeUserForMainThread] defaultType]] );
[MPAlgorithmDefault shortNameOfType:[[MPiOSAppDelegate get] activeUserForMainThread].defaultType] );
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
@ -220,7 +220,9 @@
__weak MPElementListController *wSelf = self;
[[MPiOSAppDelegate get] addElementNamed:siteName completion:^(MPElementEntity *element) {
if (element)
[wSelf.delegate didSelectElement:element];
PearlMainQueue( ^{
[wSelf.delegate didSelectElement:element];
} );
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
}

View File

@ -62,7 +62,7 @@
if (buttonIndex_ == alert.cancelButtonIndex)
return;
_switchCloudStoreProgress = [PearlOverlay showOverlayWithTitle:@"Enumerating Stores"];
_switchCloudStoreProgress = [PearlOverlay showProgressOverlayWithTitle:@"Enumerating Stores"];
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
[self switchCloudStore];
} );

View File

@ -776,7 +776,7 @@
@"If you continue, the password for this site will change. "
@"You will need to update your account's old password to the new one."
do:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
_activeElementOID = [[MPiOSAppDelegate get] changeElement:activeElement inContext:context
_activeElementOID = [[MPiOSAppDelegate get] changeElement:activeElement saveInContext:context
toType:type].objectID;
return YES;
}];

View File

@ -18,19 +18,17 @@
#import <Foundation/Foundation.h>
#import "MPEntities.h"
#import "MPCell.h"
@interface MPPasswordCell : UICollectionViewCell <UITextFieldDelegate>
@interface MPPasswordCell : MPCell
@property(nonatomic, copy) NSString *transientSite;
@property(strong, nonatomic) IBOutlet UITextField *contentField;
+ (NSString *)reuseIdentifier;
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context;
+ (NSString *)reuseIdentifierForElement:(MPElementEntity *)entity;
- (void)setElement:(MPElementEntity *)element;
@property(strong, nonatomic) IBOutlet UILabel *nameLabel;
@property(strong, nonatomic) IBOutlet UIButton *loginButton;
/** Populate our UI to reflect the current state. */
- (void)updateAnimated:(BOOL)animated;
- (void)populateWithElement:(MPElementEntity *)element;
- (void)reloadWithElement:(MPElementEntity *)mainElement;
- (void)reloadWithTransientSite:(NSString *)siteName;
@end

View File

@ -19,47 +19,9 @@
#import "MPPasswordCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPPasswordGeneratedCell.h"
#import "MPPasswordStoredCell.h"
@interface MPPasswordCell()
@property(strong, nonatomic) IBOutlet UILabel *nameLabel;
@property(strong, nonatomic) IBOutlet UIButton *loginButton;
@property(nonatomic, strong) NSManagedObjectID *elementOID;
@end
@implementation MPPasswordCell
+ (NSString *)reuseIdentifier {
return NSStringFromClass( self );
}
+ (NSString *)reuseIdentifierForElement:(MPElementEntity *)element {
if ([element isKindOfClass:[MPElementGeneratedEntity class]])
return [MPPasswordGeneratedCell reuseIdentifier];
if ([element isKindOfClass:[MPElementStoredEntity class]])
return [MPPasswordStoredCell reuseIdentifier];
return [self reuseIdentifier];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == self.contentField && [self.contentField.text length]) {
[self.contentField resignFirstResponder];
return YES;
}
return NO;
}
#pragma mark - Life cycle
- (void)awakeFromNib {
@ -67,120 +29,68 @@
[super awakeFromNib];
self.layer.cornerRadius = 5;
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0;
self.layer.shadowColor = [UIColor whiteColor].CGColor;
}
- (void)dealloc {
- (void)prepareForReuse {
[self removeKeyPathObservers];
[super prepareForReuse];
[self updateAnimated:NO];
}
#pragma mark - Actions
// Unblocks animations for all CALayer properties (eg. shadowOpacity)
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event {
- (IBAction)doUser:(id)sender {
id<CAAction> defaultAction = [super actionForLayer:layer forKey:event];
if (defaultAction == (id)[NSNull null] && [event isEqualToString:@"position"])
return defaultAction;
return NSNullToNil(defaultAction);
}
#pragma mark - Properties
- (void)setTransientSite:(NSString *)name {
- (void)setSelected:(BOOL)selected {
_transientSite = name;
_elementOID = nil;
[super setSelected:selected];
[self updateAnimated:YES];
}
- (void)setElement:(MPElementEntity *)element {
- (void)setHighlighted:(BOOL)highlighted {
self.elementOID = [element objectID];
}
- (void)setElementOID:(NSManagedObjectID *)elementOID {
_transientSite = nil;
_elementOID = elementOID;
[super setHighlighted:highlighted];
[self updateAnimated:YES];
}
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context {
NSError *error = nil;
MPElementEntity *element = _elementOID? (MPElementEntity *)[context existingObjectWithID:_elementOID error:&error]: nil;
if (_elementOID && !element)
err(@"Failed to load element: %@", error);
return element;
}
#pragma mark - Private
- (void)updateAnimated:(BOOL)animated {
Weakify(self);
if (self.transientSite) {
self.alpha = 1;
self.nameLabel.text = self.transientSite;
self.contentField.text = nil;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPKey *key = [MPiOSAppDelegate get].key;
if (!key) {
self.alpha = 0;
return;
}
MPElementType type = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType;
if (!type)
type = MPElementTypeGeneratedLong;
NSString *newContent = [MPAlgorithmDefault generateContentNamed:self.transientSite ofType:type withCounter:1 usingKey:key];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.contentField.text = newContent;
}];
if (![NSThread isMainThread]) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateAnimated:animated];
}];
return;
}
else if (self.elementOID) {
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
[mainContext performBlock:^{
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
Strongify(self);
NSError *error = nil;
MPKey *key = [MPiOSAppDelegate get].key;
if (!key) {
self.alpha = 0;
return;
}
MPElementEntity *element = (MPElementEntity *)[mainContext existingObjectWithID:_elementOID error:&error];
if (!element) {
err(@"Failed to load element: %@", error);
self.alpha = 0;
return;
}
self.alpha = 1;
[self populateWithElement:element];
[element resolveContentUsingKey:key result:^(NSString *result) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
Strongify(self);
self.contentField.text = result;
}];
}];
}];
}];
}
else {
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
self.alpha = 0;
}];
}
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
self.layer.shadowOpacity = self.selected? 1: self.highlighted? 0.3f: 0;
}];
}
- (void)populateWithElement:(MPElementEntity *)element {
- (void)reloadWithElement:(MPElementEntity *)mainElement {
self.nameLabel.text = element.name;
self.contentField.text = nil;
self.nameLabel.text = mainElement.name;
}
- (void)reloadWithTransientSite:(NSString *)siteName {
self.nameLabel.text = strl( @"%@ - Tap to create", siteName );
}
@end

View File

@ -0,0 +1,31 @@
/**
* 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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordElementCell.h
// MPPasswordElementCell
//
// Created by lhunath on 2014-04-03.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPPasswordCell.h"
@interface MPPasswordElementCell : MPPasswordCell
@property(nonatomic, copy) NSString *transientSite;
- (MPElementEntity *)mainElement;
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context;
- (void)setElement:(MPElementEntity *)element;
- (void)reloadData;
@end

View File

@ -0,0 +1,93 @@
/**
* 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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordElementCell.h
// MPPasswordElementCell
//
// Created by lhunath on 2014-04-03.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPPasswordElementCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
@implementation MPPasswordElementCell {
NSManagedObjectID *_elementOID;
}
- (void)prepareForReuse {
_elementOID = nil;
_transientSite = nil;
[super prepareForReuse];
}
- (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 {
if ([_elementOID isEqual:element.objectID])
return;
dbg(@"element: %@ -> %@", _elementOID, element.objectID);
_transientSite = nil;
_elementOID = element.objectID;
[self updateAnimated:YES];
[self reloadData];
}
- (MPElementEntity *)mainElement {
return [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
}
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context {
if (!_elementOID)
return nil;
NSError *error = nil;
MPElementEntity *element = _elementOID? (MPElementEntity *)[context existingObjectWithID:_elementOID error:&error]: nil;
if (_elementOID && !element)
err(@"Failed to load element: %@", error);
return element;
}
- (void)reloadData {
if (self.transientSite)
PearlMainQueue( ^{
[self reloadWithTransientSite:self.transientSite];
} );
else
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
[self reloadWithElement:[self elementInContext:mainContext]];
}];
}
@end

View File

@ -1,87 +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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordGeneratedCell.h
// MPPasswordGeneratedCell
//
// Created by lhunath on 2014-03-19.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPPasswordGeneratedCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
@interface MPPasswordGeneratedCell()
@property(strong, nonatomic) IBOutlet UILabel *counterLabel;
@property(strong, nonatomic) IBOutlet UIButton *counterButton;
@property(strong, nonatomic) IBOutlet UIButton *upgradeButton;
@end
@implementation MPPasswordGeneratedCell
- (void)populateWithElement:(MPElementEntity *)element {
[super populateWithElement:element];
MPElementGeneratedEntity *generatedElement = [self generatedElement:element];
self.counterLabel.text = strf(@"%lu", (unsigned long)generatedElement.counter);
if (element.requiresExplicitMigration) {
self.upgradeButton.alpha = 1;
self.counterButton.alpha = 0;
} else {
self.upgradeButton.alpha = 0;
self.counterButton.alpha = 1;
}
}
#pragma mark - Actions
- (IBAction)doUpgrade:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
[[self elementInContext:mainContext] migrateExplicitly:YES];
[mainContext saveToStore];
[self updateAnimated:YES];
}];
}
- (IBAction)doIncrementCounter:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
++[self elementInContext:mainContext].counter;
[mainContext saveToStore];
[self updateAnimated:YES];
}];
}
#pragma mark - Properties
- (MPElementGeneratedEntity *)elementInContext:(NSManagedObjectContext *)context {
return [self generatedElement:[super elementInContext:context]];
}
- (MPElementGeneratedEntity *)generatedElement:(MPElementEntity *)element {
NSAssert([element isKindOfClass:[MPElementGeneratedEntity class]], @"Element is not of generated type: %@", element.name);
if (![element isKindOfClass:[MPElementGeneratedEntity class]])
return nil;
return (MPElementGeneratedEntity *)element;
}
@end

View File

@ -0,0 +1,44 @@
/**
* 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 <lhunath@lyndir.com>
* @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 <Foundation/Foundation.h>
#import "MPEntities.h"
#import "MPCell.h"
#import "MPPasswordCell.h"
typedef NS_ENUM (NSUInteger, MPContentFieldMode) {
MPContentFieldModePassword,
MPContentFieldModeUser,
};
@interface MPPasswordLargeCell : MPPasswordCell <UITextFieldDelegate>
@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;
+ (instancetype)dequeueCellWithType:(MPElementType)type fromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath;
- (void)resolveContentOfCellTypeForTransientSite:(NSString *)siteName usingKey:(MPKey *)key result:(void (^)(NSString *))resultBlock;
- (void)resolveContentOfCellTypeForElement:(MPElementEntity *)element usingKey:(MPKey *)key result:(void (^)(NSString *))resultBlock;
- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context;
@end

View File

@ -0,0 +1,213 @@
/**
* 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 <lhunath@lyndir.com>
* @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
+ (instancetype)dequeueCellWithType:(MPElementType)type fromCollectionView:(UICollectionView *)collectionView
atIndexPath:(NSIndexPath *)indexPath {
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;
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField == self.contentField) {
NSString *newContent = textField.text;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *element = [[MPPasswordElementCell findAsSuperviewOf:self] elementInContext:context];
switch (self.contentFieldMode) {
case MPContentFieldModePassword:
break;
case MPContentFieldModeUser: {
element.loginName = newContent;
[context saveToStore];
PearlMainQueue( ^{
[self updateAnimated:YES];
[PearlOverlay showTemporaryOverlayWithTitle:@"Login Updated" dismissAfter:2];
} );
break;
}
}
}];
}
}
#pragma mark - Life cycle
- (void)prepareForReuse {
_contentFieldMode = 0;
[super prepareForReuse];
}
- (void)reloadWithTransientSite:(NSString *)siteName {
[super reloadWithTransientSite:siteName];
self.loginButton.alpha = 0;
self.upgradeButton.alpha = 0;
self.contentField.enabled = NO;
self.contentField.placeholder = strl( @"Tap to enter password" );
self.typeLabel.text = [MPAlgorithmDefault nameOfType:self.type];
[self resolveContentOfCellTypeForTransientSite:siteName usingKey:[MPiOSAppDelegate get].key result:^(NSString *string) {
PearlMainQueue( ^{ self.contentField.text = string; } );
}];
}
- (void)reloadWithElement:(MPElementEntity *)mainElement {
[super reloadWithElement:mainElement];
self.loginButton.alpha = 1;
self.typeLabel.text = [mainElement.algorithm nameOfType:self.type];
if (mainElement.requiresExplicitMigration)
self.upgradeButton.alpha = 1;
else
self.upgradeButton.alpha = 0;
switch (self.contentFieldMode) {
case MPContentFieldModePassword: {
if (self.type & MPElementTypeClassStored) {
self.contentField.enabled = YES;
self.contentField.placeholder = strl( @"Enter custom password" );
}
else if (self.type & MPElementTypeClassGenerated) {
self.contentField.enabled = NO;
self.contentField.placeholder = strl( @"Generating..." );
}
else {
self.contentField.enabled = NO;
self.contentField.placeholder = nil;
}
self.contentField.text = nil;
MPKey *key = [MPiOSAppDelegate get].key;
if (self.type == mainElement.type)
[mainElement resolveContentUsingKey:key result:^(NSString *string) {
PearlMainQueue( ^{ self.contentField.text = string; } );
}];
else
[self resolveContentOfCellTypeForElement:mainElement usingKey:key result:^(NSString *string) {
PearlMainQueue( ^{ self.contentField.text = string; } );
}];
break;
}
case MPContentFieldModeUser: {
self.contentField.enabled = YES;
self.contentField.placeholder = strl( @"Enter login name" );
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 );
}
- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context {
return [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:self.type];
}
#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 ([[[MPPasswordElementCell findAsSuperviewOf:self] elementInContext:context] migrateExplicitly:YES]) {
[context saveToStore];
PearlMainQueue( ^{
[[MPPasswordElementCell 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;
[[MPPasswordElementCell findAsSuperviewOf:self] reloadData];
}
@end

View File

@ -9,18 +9,19 @@
*/
//
// MPPasswordGeneratedCell.h
// MPPasswordGeneratedCell
// MPPasswordLargeGeneratedCell.h
// MPPasswordLargeGeneratedCell
//
// Created by lhunath on 2014-03-19.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPPasswordCell.h"
#import "MPPasswordLargeCell.h"
@interface MPPasswordGeneratedCell : MPPasswordCell
@interface MPPasswordLargeGeneratedCell : MPPasswordLargeCell
- (MPElementGeneratedEntity *)elementInContext:(NSManagedObjectContext *)context;
@property(strong, nonatomic) IBOutlet UILabel *counterLabel;
@property(strong, nonatomic) IBOutlet UIButton *counterButton;
@end

View File

@ -0,0 +1,139 @@
/**
* 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 <lhunath@lyndir.com>
* @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 "MPPasswordElementCell.h"
@implementation MPPasswordLargeGeneratedCell
- (void)awakeFromNib {
[super awakeFromNib];
UILongPressGestureRecognizer *gestureRecognizer = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(doResetCounterRecognizer:)];
[self.counterButton addGestureRecognizer:gestureRecognizer];
}
- (void)reloadWithElement:(MPElementEntity *)mainElement {
[super reloadWithElement: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 = 0;
self.counterButton.alpha = 0;
}
else {
self.counterLabel.alpha = 1;
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<MPAlgorithm> 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 != UIGestureRecognizerStateEnded)
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
- (MPElementGeneratedEntity *)generatedElementInContext:(NSManagedObjectContext *)context {
return [self generatedElement:[[MPPasswordElementCell findAsSuperviewOf:self] elementInContext:context]];
}
- (MPElementGeneratedEntity *)generatedElement:(MPElementEntity *)element {
if (![element isKindOfClass:[MPElementGeneratedEntity class]])
return nil;
return (MPElementGeneratedEntity *)element;
}
@end

View File

@ -9,18 +9,16 @@
*/
//
// MPPasswordStoredCell.h
// MPPasswordStoredCell
// MPPasswordLargeStoredCell.h
// MPPasswordLargeStoredCell
//
// Created by lhunath on 2014-03-19.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPPasswordCell.h"
#import "MPPasswordLargeCell.h"
@interface MPPasswordStoredCell : MPPasswordCell
- (MPElementStoredEntity *)elementInContext:(NSManagedObjectContext *)context;
@interface MPPasswordLargeStoredCell : MPPasswordLargeCell
@end

View File

@ -0,0 +1,112 @@
/**
* 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 <lhunath@lyndir.com>
* @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 "MPPasswordElementCell.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];
if (storedElement) {
storedElement.contentObject = self.contentField.text;
[context saveToStore];
}
return element;
}
#pragma mark - Actions
- (IBAction)doEditContent:(UIButton *)sender {
UITextField *field = self.contentField;
field.enabled = YES;
[field becomeFirstResponder];
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
[super textFieldDidEndEditing:textField];
if (textField == self.contentField) {
NSString *newContent = textField.text;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementStoredEntity *storedElement = [self storedElementInContext:context];
if (!storedElement)
return;
switch (self.contentFieldMode) {
case MPContentFieldModePassword: {
storedElement.contentObject = newContent;
[context saveToStore];
PearlMainQueue( ^{
[self updateAnimated:YES];
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
} );
break;
}
case MPContentFieldModeUser:
break;
}
}];
}
}
#pragma mark - Properties
- (MPElementStoredEntity *)storedElementInContext:(NSManagedObjectContext *)context {
return [self storedElement:[[MPPasswordElementCell findAsSuperviewOf:self] elementInContext:context]];
}
- (MPElementStoredEntity *)storedElement:(MPElementEntity *)element {
if (![element isKindOfClass:[MPElementStoredEntity class]])
return nil;
return (MPElementStoredEntity *)element;
}
@end

View File

@ -0,0 +1,33 @@
/**
* 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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordSmallCell.h
// MPPasswordSmallCell
//
// Created by lhunath on 2014-03-28.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPPasswordCell.h"
@interface MPPasswordSmallCell : MPPasswordElementCell
+ (instancetype)dequeueCellForElement:(MPElementEntity *)element
fromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath;
@end
@interface MPPasswordSmallGeneratedCell : MPPasswordSmallCell
@end
@interface MPPasswordSmallStoredCell : MPPasswordSmallCell
@end

View File

@ -0,0 +1,48 @@
/**
* 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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordSmallCell.h
// MPPasswordSmallCell
//
// Created by lhunath on 2014-03-28.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPPasswordElementCell.h"
#import "MPPasswordSmallCell.h"
@implementation MPPasswordSmallCell {
}
+ (instancetype)dequeueCellForElement:(MPElementEntity *)element fromCollectionView:(UICollectionView *)collectionView
atIndexPath:(NSIndexPath *)indexPath {
NSString *reuseIdentifier;
if (element.type & MPElementTypeClassGenerated)
reuseIdentifier = NSStringFromClass( [MPPasswordSmallGeneratedCell class] );
else if (element.type & MPElementTypeClassStored)
reuseIdentifier = NSStringFromClass( [MPPasswordSmallStoredCell class] );
else
Throw(@"Unexpected password type: %@", [MPAlgorithmDefault nameOfType:element.type]);
MPPasswordSmallCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
[cell setElement:element];
return cell;
}
@end
@implementation MPPasswordSmallGeneratedCell
@end
@implementation MPPasswordSmallStoredCell
@end

View File

@ -1,73 +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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordGeneratedCell.h
// MPPasswordGeneratedCell
//
// Created by lhunath on 2014-03-19.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPPasswordStoredCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
@interface MPPasswordStoredCell()
@property(strong, nonatomic) IBOutlet UIButton *editButton;
@end
@implementation MPPasswordStoredCell
#pragma mark - Actions
- (IBAction)doEditContent:(UIButton *)sender {
self.contentField.enabled = YES;
[self.contentField becomeFirstResponder];
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
[super textFieldDidEndEditing:textField];
if (textField == self.contentField) {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
if (mainContext) {
[[self elementInContext:mainContext] setContentObject:self.contentField.text];
[mainContext saveToStore];
}
[self updateAnimated:YES];
}];
}
}
#pragma mark - Properties
- (MPElementStoredEntity *)elementInContext:(NSManagedObjectContext *)context {
return [self storedElement:[super elementInContext:context]];
}
- (MPElementStoredEntity *)storedElement:(MPElementEntity *)element {
NSAssert([element isKindOfClass:[MPElementStoredEntity class]], @"Element is not of generated type: %@", element.name);
if (![element isKindOfClass:[MPElementStoredEntity class]])
return nil;
return (MPElementStoredEntity *)element;
}
@end

View File

@ -0,0 +1,34 @@
/**
* 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 <lhunath@lyndir.com>
* @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 <Foundation/Foundation.h>
#import "MPCell.h"
#import "MPPasswordCell.h"
#import "MPPasswordElementCell.h"
@interface MPPasswordTypesCell : MPPasswordElementCell <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property(nonatomic, strong) IBOutlet UICollectionView *contentCollectionView;
@property(nonatomic, strong) id<MPAlgorithm> algorithm;
+ (instancetype)dequeueCellForElement:(MPElementEntity *)element
fromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath;
+ (instancetype)dequeueCellForTransientSite:(NSString *)siteName
fromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath;
@end

View File

@ -0,0 +1,218 @@
/**
* 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 <lhunath@lyndir.com>
* @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"
@implementation MPPasswordTypesCell
#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 {
_algorithm = MPAlgorithmDefault;
[super prepareForReuse];
}
- (void)reloadWithTransientSite:(NSString *)siteName {
[super reloadWithTransientSite:siteName];
[self.contentCollectionView reloadData];
NSIndexPath *visibleIndexPath = [self contentIndexPathForType:
IfElse([[MPiOSAppDelegate get] activeUserForMainThread].defaultType, MPElementTypeGeneratedLong)];
[self.contentCollectionView scrollToItemAtIndexPath:visibleIndexPath atScrollPosition:NO
animated:UICollectionViewScrollPositionCenteredHorizontally];
}
- (void)reloadWithElement:(MPElementEntity *)mainElement {
[super reloadWithElement:mainElement];
self.algorithm = IfNotNilElse([self mainElement].algorithm, MPAlgorithmDefault);
[self.contentCollectionView reloadData];
NSIndexPath *visibleIndexPath = [self contentIndexPathForType:mainElement.type];
[self.contentCollectionView scrollToItemAtIndexPath:visibleIndexPath atScrollPosition:NO
animated:UICollectionViewScrollPositionCenteredHorizontally];
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (!self.algorithm)
dbg_return_tr(0, @, @(section));
NSInteger types = 1;
MPElementType type = [self typeForContentIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
for (MPElementType nextType = type; type != (nextType = [self.algorithm nextType:nextType]);)
++types;
dbg_return_tr(types, @, @(section));
}
- (MPPasswordLargeCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MPPasswordLargeCell *cell = [MPPasswordLargeCell dequeueCellWithType:[self typeForContentIndexPath:indexPath]
fromCollectionView:collectionView atIndexPath:indexPath];
if (self.transientSite)
[cell reloadWithTransientSite:self.transientSite];
else
[cell reloadWithElement:self.mainElement];
dbg_return(cell, indexPath);
}
#pragma mark - UICollectionViewDelegateFlowLayout
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionView *passwordCollectionView = [UICollectionView findAsSuperviewOf:self];
[passwordCollectionView.delegate collectionView:passwordCollectionView didSelectItemAtIndexPath:[passwordCollectionView indexPathForCell:self]];
}
#pragma mark - UIScrollViewDelegate
- (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 = CGPointFromCGRectTopLeft(
[self.contentCollectionView layoutAttributesForItemAtIndexPath:targetIndexPath].frame );
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (scrollView == self.contentCollectionView && !decelerate)
[self saveContentType];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.contentCollectionView)
[self saveContentType];
}
#pragma mark - Private
- (MPElementType)typeForContentIndexPath:(NSIndexPath *)indexPath {
MPElementType type = MPElementTypeGeneratedPIN;
for (NSUInteger i = 0; i < indexPath.item; ++i)
type = [self.algorithm nextType:type];
return type;
}
- (NSIndexPath *)contentIndexPathForType:(MPElementType)type {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
while ([self typeForContentIndexPath:indexPath] != type) {
indexPath = [NSIndexPath indexPathForItem:indexPath.item + 1 inSection:indexPath.section];
NSAssert1(indexPath.item < [self.contentCollectionView numberOfItemsInSection:0],
@"No item found for type: %@", [self.algorithm nameOfType:type]);
}
return indexPath;
}
- (void)saveContentType {
if (self.transientSite)
return;
CGPoint centerPoint = CGPointFromCGRectCenter( self.contentCollectionView.bounds );
NSIndexPath *centerIndexPath = [self.contentCollectionView indexPathForItemAtPoint:centerPoint];
[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.type == cell.type)
// Nothing changed.
return;
self.element = [cell saveContentTypeWithElement:element saveInContext:context];
}];
}
#pragma mark - Properties
- (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<MPAlgorithm>)algorithm {
if ([_algorithm isEqual:algorithm])
return;
_algorithm = algorithm;
[self.contentCollectionView reloadData];
}
@end

View File

@ -17,8 +17,9 @@
//
#import "LLGitTip.h"
@class MPElementEntity;
@interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UITextFieldDelegate>
@interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property(strong, nonatomic) IBOutlet UIView *passwordSelectionContainer;
@property(strong, nonatomic) IBOutlet UICollectionView *passwordCollectionView;

View File

@ -19,14 +19,17 @@
#import "MPPasswordsViewController.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPPasswordCell.h"
#import "MPPasswordLargeCell.h"
#import "UIScrollView+PearlAdjustInsets.h"
#import "MPPasswordTypesCell.h"
#import "MPPasswordSmallCell.h"
#import "UIColor+Expanded.h"
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
@property(strong, nonatomic) IBOutlet UINavigationBar *navigationBar;
@property(nonatomic, strong) IBOutlet UINavigationBar *navigationBar;
@property(nonatomic, readonly) NSString *query;
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@end
@implementation MPPasswordsViewController {
@ -34,13 +37,23 @@
__weak id _mocObserver;
NSArray *_notificationObservers;
__weak UITapGestureRecognizer *_passwordsDismissRecognizer;
NSFetchedResultsController *_fetchedResultsController;
NSManagedObjectID *_activeElementOID;
BOOL _exactMatch;
NSMutableDictionary *_fetchedUpdates;
UIColor *_backgroundColor;
UIColor *_darkenedBackgroundColor;
}
- (void)viewDidLoad {
[super viewDidLoad];
_fetchedUpdates = [NSMutableDictionary dictionaryWithCapacity:4];
_darkenedBackgroundColor = [(_backgroundColor = [UIColor colorWithRGBHex:0x1F2124]) colorByMultiplyingBy:0.7f];
self.view.backgroundColor = [UIColor clearColor];
[self.passwordCollectionView automaticallyAdjustInsetsForKeyboard];
}
- (void)viewWillAppear:(BOOL)animated {
@ -59,74 +72,260 @@
[self stopObservingStore];
}
#pragma mark - UITextFieldDelegate
#pragma mark - UICollectionViewDelegateFlowLayout
- (void)textFieldDidEndEditing:(UITextField *)textField {
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (collectionView == self.passwordCollectionView) {
if (indexPath.item < 3 ||
indexPath.item >= ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
return CGSizeMake( 300, 100 );
return NO;
}
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
return CGSizeMake( (300 - layout.minimumInteritemSpacing) / 2, 44 );
}
// This isn't really in UITextFieldDelegate. We fake it from UITextFieldTextDidChangeNotification.
- (void)textFieldDidChange:(UITextField *)textField {
Throw(@"Unexpected collection view: %@", collectionView);
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return [self.fetchedResultsController.sections count] + 1;
if (collectionView == self.passwordCollectionView)
dbg_return_tr([self.fetchedResultsController.sections count], @);
Throw(@"Unexpected collection view: %@", collectionView);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (collectionView == self.passwordCollectionView) {
if (section < [self.fetchedResultsController.sections count])
return ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[section]).numberOfObjects;
if (collectionView == self.passwordCollectionView)
dbg_return_tr(!self.query.length? 0: ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[section]).numberOfObjects +
(_exactMatch? 0: 1), @, @(section));
// New Site.
return [self.query length]? 1: 0;
}
Throw(@"unexpected collection view: %@", collectionView);
Throw(@"Unexpected collection view: %@", collectionView);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.passwordCollectionView) {
MPPasswordCell *cell;
if (indexPath.section < [self.fetchedResultsController.sections count]) {
[UIView setAnimationsEnabled:NO];
MPPasswordElementCell *cell;
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) {
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (indexPath.item < 2)
cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPPasswordCell reuseIdentifierForElement:element] forIndexPath:indexPath];
if (indexPath.item < 3)
cell = [MPPasswordTypesCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath];
else
cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPPasswordCell reuseIdentifierForElement:element] forIndexPath:indexPath];
[cell setElement:element];
} else {
// New Site.
cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPPasswordCell reuseIdentifier] forIndexPath:indexPath];
cell.transientSite = self.query;
cell = [MPPasswordSmallCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath];
}
return cell;
else
// New Site.
cell = [MPPasswordTypesCell dequeueCellForTransientSite:self.query fromCollectionView:collectionView atIndexPath:indexPath];
[UIView setAnimationsEnabled:YES];
dbg_return(cell, indexPath);
}
Throw(@"unexpected collection view: %@", collectionView);
Throw(@"Unexpected collection view: %@", collectionView);
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
MPPasswordElementCell *cell = (MPPasswordElementCell *)[collectionView cellForItemAtIndexPath:indexPath];
NSString *newSiteName = cell.transientSite;
if (newSiteName) {
[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
[collectionView selectItemAtIndexPath:indexPath animated:NO
scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
return;
}
// Create
[[MPiOSAppDelegate get] addElementNamed:newSiteName completion:^(MPElementEntity *element) {
self.activeElement = element;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[PearlOverlay showTemporaryOverlayWithTitle:strf( @"Added %@", newSiteName ) dismissAfter:2];
[collectionView selectItemAtIndexPath:indexPath animated:NO
scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
}];
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
return;
}
MPElementEntity *element = [cell mainElement];
if (!element) {
[collectionView selectItemAtIndexPath:indexPath animated:NO
scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
return;
}
inf(@"Copying password for: %@", element.name);
MPCheckpoint( MPCheckpointCopyToPasteboard, @{
@"type" : NilToNSNull(element.typeName),
@"version" : @(element.version),
@"emergency" : @NO
} );
[element use];
[element resolveContentUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
if (![result length]) {
[collectionView selectItemAtIndexPath:indexPath animated:NO
scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
return;
}
[UIPasteboard generalPasteboard].string = result;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Copied" dismissAfter:2];
PearlMainQueueAfter( 0.1f, ^{
[collectionView selectItemAtIndexPath:indexPath animated:NO
scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
} );
}];
}];
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
if (controller == _fetchedResultsController) {
dbg(@"controllerWillChangeContent");
NSAssert(![_fetchedUpdates count], @"Didn't finish a previous change update?");
if ([_fetchedUpdates count]) {
[_fetchedUpdates removeAllObjects];
[self.passwordCollectionView reloadData];
}
}
}
#pragma mark - UILongPressGestureRecognizer
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
- (void)didLongPress:(UILongPressGestureRecognizer *)recognizer {
if (controller == _fetchedResultsController) {
NSMutableArray *updatesForType = _fetchedUpdates[@(type)];
if (!updatesForType)
_fetchedUpdates[@(type)] = updatesForType = [NSMutableArray new];
[updatesForType addObject:@{ @"object" : NilToNSNull(anObject),
@"indexPath" : NilToNSNull(indexPath),
@"newIndexPath" : NilToNSNull(newIndexPath) }];
switch (type) {
case NSFetchedResultsChangeInsert:
dbg(@"didChangeObject: insert: %@", [updatesForType lastObject]);
break;
case NSFetchedResultsChangeDelete:
dbg(@"didChangeObject: delete: %@", [updatesForType lastObject]);
break;
case NSFetchedResultsChangeMove:
dbg(@"didChangeObject: move: %@", [updatesForType lastObject]);
break;
case NSFetchedResultsChangeUpdate:
dbg(@"didChangeObject: update: %@", [updatesForType lastObject]);
break;
}
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
if (controller == _fetchedResultsController) {
NSMutableArray *updatesForType = _fetchedUpdates[@(type << 3)];
if (!updatesForType)
_fetchedUpdates[@(type << 3)] = updatesForType = [NSMutableArray new];
[updatesForType addObject:@{ @"sectionInfo" : NilToNSNull(sectionInfo),
@"index" : @(sectionIndex) }];
switch (type) {
case NSFetchedResultsChangeInsert:
dbg(@"didChangeSection: insert: %@", [updatesForType lastObject]);
break;
case NSFetchedResultsChangeDelete:
dbg(@"didChangeSection: delete: %@", [updatesForType lastObject]);
break;
case NSFetchedResultsChangeMove:
dbg(@"didChangeSection: move: %@", [updatesForType lastObject]);
break;
case NSFetchedResultsChangeUpdate:
dbg(@"didChangeSection: update: %@", [updatesForType lastObject]);
break;
}
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
if (controller == _fetchedResultsController && [_fetchedUpdates count]) {
[self.passwordCollectionView performBatchUpdates:^{
[_fetchedUpdates enumerateKeysAndObjectsUsingBlock:^(NSNumber *typeNumber, NSArray *updates, BOOL *stop) {
BOOL updateIsSection = NO;
NSFetchedResultsChangeType type = [typeNumber unsignedIntegerValue];
if (type >= 1 << 3) {
updateIsSection = YES;
type = type >> 3;
}
switch (type) {
case NSFetchedResultsChangeInsert:
if (updateIsSection) {
for (NSDictionary *update in updates) {
dbg(@"insertSections:%@", update[@"index"]);
[self.passwordCollectionView insertSections:
[NSIndexSet indexSetWithIndex:[update[@"index"] unsignedIntegerValue]]];
}
}
else {
dbg(@"insertItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.newIndexPath"]);
[self.passwordCollectionView insertItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.newIndexPath"]];
}
break;
case NSFetchedResultsChangeDelete:
if (updateIsSection) {
for (NSDictionary *update in updates) {
dbg(@"deleteSections:%@", update[@"index"]);
[self.passwordCollectionView deleteSections:
[NSIndexSet indexSetWithIndex:[update[@"index"] unsignedIntegerValue]]];
}
}
else {
dbg(@"deleteItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.indexPath"]);
[self.passwordCollectionView deleteItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.indexPath"]];
}
break;
case NSFetchedResultsChangeMove:
NSAssert(!updateIsSection, @"Move not supported for sections");
for (NSDictionary *update in updates) {
dbg(@"moveItemAtIndexPath:%@ toIndexPath:%@", update[@"indexPath"], update[@"newIndexPath"]);
[self.passwordCollectionView moveItemAtIndexPath:update[@"indexPath"] toIndexPath:update[@"newIndexPath"]];
}
break;
case NSFetchedResultsChangeUpdate:
NSAssert(!updateIsSection, @"Update not supported for sections");
dbg(@"reloadItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.indexPath"]);
[self.passwordCollectionView reloadItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.indexPath"]];
break;
}
}];
} completion:nil];
[_fetchedUpdates removeAllObjects];
}
}
#pragma mark - UIScrollViewDelegate
#pragma mark - UISearchBarDelegate
@ -138,9 +337,9 @@
self.passwordsSearchBar.showsCancelButton = YES;
_passwordsDismissRecognizer = [self.view dismissKeyboardForField:self.passwordsSearchBar onTouchForced:NO];
// [UIView animateWithDuration:0.3f animations:^{
// self.passwordCollectionView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.3f];
// }];
[UIView animateWithDuration:0.3f animations:^{
searchBar.superview.backgroundColor = _darkenedBackgroundColor;
}];
}
}
@ -152,7 +351,7 @@
[self.view removeGestureRecognizer:_passwordsDismissRecognizer];
[UIView animateWithDuration:0.3f animations:^{
self.passwordCollectionView.backgroundColor = [UIColor clearColor];
searchBar.superview.backgroundColor = _backgroundColor;
}];
}
}
@ -195,12 +394,22 @@
self.passwordSelectionContainer.alpha = 0;
}],
[[NSNotificationCenter defaultCenter]
addObserverForName:MPSignedOutNotification object:nil
queue:nil usingBlock:^(NSNotification *note) {
Strongify(self);
self.activeElement = nil;
_fetchedResultsController = nil;
self.passwordsSearchBar.text = nil;
[self updatePasswords];
}],
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
// [self updateMode]; TODO: reload passwords list
[self updatePasswords];
[UIView animateWithDuration:1 animations:^{
self.passwordSelectionContainer.alpha = 1;
}];
@ -217,22 +426,22 @@
- (void)observeStore {
Weakify(self);
Weakify(self);
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
if (!_mocObserver && mainContext)
_mocObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Strongify(self);
// [self updateMode]; TODO: reload passwords list
Strongify(self);
[self updatePasswords];
}];
if (!_storeObserver)
_storeObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
self.fetchedResultsController = nil;
_fetchedResultsController = nil;
[self updatePasswords];
}];
}
@ -247,20 +456,48 @@
- (void)updatePasswords {
[self.fetchedResultsController.managedObjectContext performBlock:^{
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
if (!activeUserOID)
return;
NSString *query = self.query;
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
if (!activeUserOID || ![query length]) {
self.passwordsSearchBar.text = nil;
PearlMainQueue( ^{ [self.passwordCollectionView reloadData]; } );
return;
}
[self.fetchedResultsController.managedObjectContext performBlock:^{
NSError *error = nil;
self.fetchedResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat:
@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, self.query];
@"user == %@ AND name BEGINSWITH[cd] %@", activeUserOID, query];
if (![self.fetchedResultsController performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.passwordCollectionView reloadData];
}];
_exactMatch = NO;
for (MPElementEntity *entity in self.fetchedResultsController.fetchedObjects)
if ([entity.name isEqualToString:query]) {
_exactMatch = YES;
break;
}
PearlMainQueue( ^{
[self.passwordCollectionView performBatchUpdates:^{
NSInteger fromSections = self.passwordCollectionView.numberOfSections;
NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView];
for (int section = 0; section < MAX(toSections, fromSections); section++) {
if (section >= fromSections) {
dbg(@"insertSections:%d", section);
[self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
}
else if (section >= toSections) {
dbg(@"deleteSections:%d", section);
[self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
}
else {
dbg(@"reloadSections:%d", section);
[self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
}
}
} completion:nil];
} );
}];
}
@ -273,21 +510,30 @@
- (NSFetchedResultsController *)fetchedResultsController {
if (!_fetchedResultsController)
[MPiOSAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
if (!_fetchedResultsController) {
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.sortDescriptors = @[
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO]
];
fetchRequest.fetchBatchSize = 10;
_fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
}];
[self observeStore];
}
return _fetchedResultsController;
}
- (void)setActiveElement:(MPElementEntity *)activeElement {
_activeElementOID = activeElement.objectID;
[self updatePasswords];
}
- (void)setActive:(BOOL)active {
[self setActive:active animated:YES];

View File

@ -102,11 +102,7 @@
UILabel *alertNameLabel = [self.nameLabel cloneAddedTo:container];
alertNameLabel.center = alertAvatar.center;
alertNameLabel.text = user.name;
alertNameLabel.bounds = CGRectSetHeight( alertNameLabel.bounds,
[alertNameLabel.text sizeWithFont:self.nameLabel.font
constrainedToSize:CGSizeMake( alertNameLabel.bounds.size.width - 10,
100 )
lineBreakMode:self.nameLabel.lineBreakMode].height );
[alertNameLabel sizeToFit];
alertNameLabel.layer.cornerRadius = 5;
alertNameLabel.backgroundColor = [UIColor blackColor];
}
@ -614,10 +610,7 @@
// Lay out user name label.
self.nameLabel.text = targetedAvatar? (targetedUser? targetedUser.name: @"New User"): nil;
self.nameLabel.bounds = CGRectSetHeight( self.nameLabel.bounds,
[self.nameLabel.text sizeWithFont:self.nameLabel.font
constrainedToSize:CGSizeMake( self.nameLabel.bounds.size.width - 10, 100 )
lineBreakMode:self.nameLabel.lineBreakMode].height );
[self.nameLabel sizeToFit];
self.oldNameLabel.bounds = self.nameLabel.bounds;
if (completion)
completion( YES );
@ -725,7 +718,7 @@
- (void)setSpinnerActive:(BOOL)active {
PearlMainThread(^{
PearlMainQueue( ^{
CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotate.toValue = [NSNumber numberWithDouble:2 * M_PI];
rotate.duration = 5.0;
@ -751,7 +744,7 @@
else
[self avatarForUser:[self selectedUserForThread]].backgroundColor = self.avatarTemplate.backgroundColor;
}];
});
} );
}
- (void)updateAvatarShadowColor:(UIButton *)avatar isTargeted:(BOOL)targeted {

View File

@ -52,15 +52,18 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
__weak id _mocObserver;
NSArray *_notificationObservers;
NSString *_masterPasswordChoice;
NSOperationQueue *_afterUpdates;
}
- (void)viewDidLoad {
[super viewDidLoad];
_afterUpdates = [NSOperationQueue new];
self.marqueeTipTexts = @[
strl(@"Press and hold to change password or delete."),
strl(@"Shake for emergency generator."),
strl( @"Press and hold to change password or delete." ),
strl( @"Shake for emergency generator." ),
];
self.view.backgroundColor = [UIColor clearColor];
@ -81,7 +84,8 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
[self reloadUsers];
[self.marqueeTipTimer invalidate];
self.marqueeTipTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(firedMarqueeTimer:) userInfo:nil repeats:YES];
self.marqueeTipTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(firedMarqueeTimer:) userInfo:nil
repeats:YES];
}
- (void)viewWillDisappear:(BOOL)animated {
@ -119,6 +123,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
break;
}
case MPActiveUserStateLogin: {
[self.entryField endEditing:YES];
[self selectedAvatar].spinnerActive = YES;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
BOOL signedIn = NO, isNew = NO;
@ -178,6 +183,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
return NO;
}
[self.entryField endEditing:YES];
[self selectedAvatar].spinnerActive = YES;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
BOOL isNew = NO;
@ -240,7 +246,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
}
}
#pragma mark - UICollectionViewDataSource
#pragma mark - UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
@ -253,6 +259,8 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
Throw(@"unexpected collection view: %@", collectionView);
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (collectionView == self.avatarCollectionView)
@ -272,11 +280,9 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
BOOL isNew = NO;
MPUserEntity *user = [self userForIndexPath:indexPath inContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
isNew:&isNew];
if (isNew) {
// New User
if (isNew)
// New User
cell.avatar = MPAvatarAdd;
cell.name = strl( @"New User" );
}
else {
// Existing User
cell.avatar = user.avatar;
@ -305,6 +311,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
BOOL isNew = NO;
MPUserEntity *user = [self userForIndexPath:indexPath inContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
isNew:&isNew];
if (isNew)
self.activeUserState = MPActiveUserStateUserName;
else if (!user.keyID)
@ -316,9 +323,8 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.avatarCollectionView) {
if (collectionView == self.avatarCollectionView)
self.activeUserState = MPActiveUserStateNone;
}
}
#pragma mark - UILongPressGestureRecognizer
@ -363,13 +369,12 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
MPUserEntity *user_ = (MPUserEntity *)[context existingObjectWithID:userID error:NULL];
if (user_)
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
dbg(@"changing mp for user: %@, keyID: %@", user_.name, user_.keyID);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
PearlMainQueue( ^{
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
scrollPosition:UICollectionViewScrollPositionNone];
[self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath];
}];
} );
}];
}];
} cancelTitle:[PearlStrings get].commonButtonCancel
@ -383,9 +388,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
targetContentOffset:(inout CGPoint *)targetContentOffset {
if (scrollView == self.avatarCollectionView) {
CGPoint offsetToCenter = CGPointMake(
self.avatarCollectionView.bounds.size.width / 2,
self.avatarCollectionView.bounds.size.height / 2 );
CGPoint offsetToCenter = self.avatarCollectionView.center;
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForItemAtPoint:
CGPointPlusCGPoint( *targetContentOffset, offsetToCenter )];
CGPoint targetCenter = [self.avatarCollectionView layoutAttributesForItemAtIndexPath:avatarIndexPath].center;
@ -502,6 +505,13 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
}
}
- (void)afterUpdatesMainQueue:(void (^)(void))block {
[_afterUpdates addOperationWithBlock:^{
PearlMainQueue( block );
}];
}
- (void)registerObservers {
if ([_notificationObservers count])
@ -555,7 +565,13 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
[self reloadUsers];
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
if ([[NSSetUnion(insertedObjects, deletedObjects)
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject isKindOfClass:[MPUserEntity class]];
}]] count])
[self reloadUsers];
}];
if (!_storeObserver)
_storeObserver = [[NSNotificationCenter defaultCenter]
@ -576,20 +592,25 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
- (void)reloadUsers {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
if (!users) {
err(@"Failed to load users: %@", error);
self.userIDs = nil;
}
[self afterUpdatesMainQueue:^{
[self observeStore];
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[
[NSSortDescriptor sortDescriptorWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO]
];
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
if (!users) {
err(@"Failed to load users: %@", error);
self.userIDs = nil;
}
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
for (MPUserEntity *user in users)
[userIDs addObject:user.objectID];
self.userIDs = userIDs;
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
for (MPUserEntity *user in users)
[userIDs addObject:user.objectID];
self.userIDs = userIDs;
}];
}];
}
@ -616,13 +637,23 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
_userIDs = userIDs;
dbg(@"userIDs -> %lu", (unsigned long)[userIDs count]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
PearlMainQueue( ^{
BOOL isNew = NO;
NSManagedObjectID *selectUserID = [MPiOSAppDelegate get].activeUserOID;
if (!selectUserID)
selectUserID = [self selectedUserInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
isNew:&isNew].objectID;
[self.avatarCollectionView reloadData];
NSUInteger selectedAvatarItem = isNew? [_userIDs count]: selectUserID? [_userIDs indexOfObject:selectUserID]: NSNotFound;
if (selectedAvatarItem != NSNotFound)
[self.avatarCollectionView selectItemAtIndexPath:[NSIndexPath indexPathForItem:selectedAvatarItem inSection:0] animated:NO
scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
[UIView animateWithDuration:0.3f animations:^{
self.userSelectionContainer.alpha = 1;
}];
}];
} );
}
- (void)setActiveUserState:(MPActiveUserState)activeUserState {
@ -635,11 +666,14 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
_activeUserState = activeUserState;
_masterPasswordChoice = nil;
if (activeUserState != MPActiveUserStateMinimized && [MPiOSAppDelegate get].key) {
if (activeUserState != MPActiveUserStateMinimized && (!self.active || [MPiOSAppDelegate get].activeUserOID)) {
[[MPiOSAppDelegate get] signOutAnimated:YES];
return;
}
[_afterUpdates setSuspended:YES];
dbg(@"suspend updates");
__block BOOL requestFirstResponder = NO;
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
MPAvatarCell *selectedAvatar = [self selectedAvatar];
@ -647,7 +681,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
for (NSUInteger item = 0; item < [self.avatarCollectionView numberOfItemsInSection:0]; ++item) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:0];
MPAvatarCell *avatarCell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
[self updateModeForAvatar:avatarCell atIndexPath:indexPath animated:NO];
[self updateModeForAvatar:avatarCell atIndexPath:indexPath animated:animated];
if (selectedAvatar && avatarCell == selectedAvatar)
[self.avatarCollectionView scrollToItemAtIndexPath:indexPath
@ -693,16 +727,6 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
break;
}
// Manage the random avatar for the new user if selected.
if (selectedAvatar.avatar == MPAvatarAdd)
selectedAvatar.avatar = arc4random() % MPAvatarCount;
else {
NSIndexPath *newUserIndexPath = [NSIndexPath indexPathForItem:[_userIDs count] inSection:0];
MPAvatarCell *newUserAvatar = (MPAvatarCell *)[[self avatarCollectionView] cellForItemAtIndexPath:newUserIndexPath];
newUserAvatar.avatar = MPAvatarAdd;
newUserAvatar.name = strl( @"New User" );
}
// Manage the entry container depending on whether a user is activate or not.
switch (activeUserState) {
case MPActiveUserStateNone: {
@ -722,6 +746,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
self.avatarCollectionView.scrollEnabled = NO;
self.entryContainer.alpha = 1;
self.footerContainer.alpha = 1;
requestFirstResponder = YES;
break;
}
case MPActiveUserStateMinimized: {
@ -737,12 +762,15 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
[self.avatarCollectionCenterConstraint apply];
// Toggle the keyboard.
if (!self.entryContainer.alpha)
[self.entryField resignFirstResponder];
} completion:^(BOOL finished) {
if (finished && self.entryContainer.alpha)
[self.entryField becomeFirstResponder];
dbg(@"resume updates");
[_afterUpdates setSuspended:NO];
}];
if (requestFirstResponder)
[self.entryField becomeFirstResponder];
else
[self.entryField resignFirstResponder];
}
#pragma mark - Actions

View File

@ -13,9 +13,10 @@
@interface MPiOSAppDelegate()
@property(nonatomic, strong) PearlAlert *handleCloudContentAlert;
@property(nonatomic, strong) PearlAlert *fixCloudContentAlert;
@property(nonatomic, strong) PearlOverlay *storeLoading;
@property(nonatomic, weak) PearlAlert *handleCloudDisabledAlert;
@property(nonatomic, weak) PearlAlert *handleCloudContentAlert;
@property(nonatomic, weak) PearlAlert *fixCloudContentAlert;
@property(nonatomic, weak) PearlOverlay *storeLoading;
@end
@implementation MPiOSAppDelegate
@ -120,12 +121,14 @@
UIImage *navBarImage = [[UIImage imageNamed:@"ui_navbar_container"] resizableImageWithCapInsets:UIEdgeInsetsMake( 0, 5, 0, 5 )];
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:navBarImage forBarMetrics:UIBarMetricsLandscapePhone];
NSShadow *titleShadow = [NSShadow new];
titleShadow.shadowColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.8f];
titleShadow.shadowOffset = CGSizeMake( 0, -1 );
[[UINavigationBar appearance] setTitleTextAttributes:
@{
UITextAttributeTextColor : [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f],
UITextAttributeTextShadowColor : [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.8f],
UITextAttributeTextShadowOffset : [NSValue valueWithUIOffset:UIOffsetMake( 0, -1 )],
UITextAttributeFont : [UIFont fontWithName:@"Exo-Bold" size:20.0f]
NSForegroundColorAttributeName : [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f],
NSShadowAttributeName : titleShadow,
NSFontAttributeName : [UIFont fontWithName:@"Exo2.0-Bold" size:20.0f]
}];
UIImage *navBarButton = [[UIImage imageNamed:@"ui_navbar_button"] resizableImageWithCapInsets:UIEdgeInsetsMake( 0, 5, 0, 5 )];
@ -136,13 +139,15 @@
setBackButtonBackgroundImage:navBarBack forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance]
setBackButtonBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
NSShadow *barButtonShadow = [NSShadow new];
barButtonShadow.shadowColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f];
barButtonShadow.shadowOffset = CGSizeMake( 0, 1 );
[[UIBarButtonItem appearance] setTitleTextAttributes:
@{
UITextAttributeTextColor : [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f],
UITextAttributeTextShadowColor : [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f],
UITextAttributeTextShadowOffset : [NSValue valueWithUIOffset:UIOffsetMake( 0, 1 )]//,
NSForegroundColorAttributeName : [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f],
NSShadowAttributeName : barButtonShadow,
// Causes a bug in iOS where image views get oddly stretched... or something.
//UITextAttributeFont: [UIFont fontWithName:@"HelveticaNeue" size:13.0f]
//NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue" size:13.0f]
}
forState:UIControlStateNormal];
@ -257,7 +262,7 @@
if (!importedSitesData)
return;
PearlOverlay *activityOverlay = [PearlOverlay showOverlayWithTitle:@"Importing"];
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
@ -463,9 +468,9 @@
@"--\n"
@"%@"
@"Master Password %@, build %@",
userName? ([userName stringByAppendingString:@"\n"]): @"",
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion )
userName? ([userName stringByAppendingString:@"\n"]): @"",
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion )
attachments:(logs
? [[PearlEMailAttachment alloc]
@ -473,8 +478,8 @@
dataUsingEncoding:NSUTF8StringEncoding]
mimeType:@"text/plain"
fileName:PearlString( @"%@-%@.log",
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]],
[PearlKeyChain deviceIdentifier] )]
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]],
[PearlKeyChain deviceIdentifier] )]
: nil), nil]
showComposerForVC:viewController];
}
@ -526,17 +531,17 @@
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
else
message = PearlString( @"Backup of Master Password sites.\n\n\n"
@"--\n"
@"%@\n"
@"Master Password %@, build %@",
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
[self activeUserForMainThread].name,
[PearlInfoPlist get].CFBundleShortVersionString,
[PearlInfoPlist get].CFBundleVersion );
NSDateFormatter *exportDateFormatter = [NSDateFormatter new];
[exportDateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
@ -545,7 +550,7 @@
attachments:[[PearlEMailAttachment alloc] initWithContent:[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
mimeType:@"text/plain" fileName:
PearlString( @"%@ (%@).mpsites", [self activeUserForMainThread].name,
[exportDateFormatter stringFromDate:[NSDate date]] )],
[exportDateFormatter stringFromDate:[NSDate date]] )],
nil];
}
@ -727,7 +732,7 @@
dispatch_async( dispatch_get_main_queue(), ^{
[self.handleCloudContentAlert cancelAlertAnimated:YES];
if (![self.storeLoading isVisible])
self.storeLoading = [PearlOverlay showOverlayWithTitle:@"Loading Sites"];
self.storeLoading = [PearlOverlay showProgressOverlayWithTitle:@"Loading Sites"];
} );
[super ubiquityStoreManager:manager willLoadStoreIsCloud:isCloudStore];
@ -739,32 +744,47 @@
[MPiOSConfig get].iCloudEnabled = @(isCloudStore);
[super ubiquityStoreManager:manager didLoadStoreForCoordinator:coordinator isCloud:isCloudStore];
dispatch_async( dispatch_get_main_queue(), ^{
[self.handleCloudContentAlert cancelAlertAnimated:YES];
[self.fixCloudContentAlert cancelAlertAnimated:YES];
[self.storeLoading cancelOverlayAnimated:YES];
} );
[self.handleCloudContentAlert cancelAlertAnimated:YES];
[self.fixCloudContentAlert cancelAlertAnimated:YES];
[self.storeLoading cancelOverlayAnimated:YES];
[self.handleCloudDisabledAlert cancelAlertAnimated:YES];
}
- (void)ubiquityStoreManager:(UbiquityStoreManager *)manager failedLoadingStoreWithCause:(UbiquityStoreErrorCause)cause context:(id)context
wasCloud:(BOOL)wasCloudStore {
dispatch_async( dispatch_get_main_queue(), ^{
[self.storeLoading cancelOverlayAnimated:YES];
} );
[self.storeLoading cancelOverlayAnimated:YES];
[self.handleCloudDisabledAlert cancelAlertAnimated:YES];
}
- (BOOL)ubiquityStoreManager:(UbiquityStoreManager *)manager handleCloudContentCorruptionWithHealthyStore:(BOOL)storeHealthy {
if (manager.cloudEnabled && !storeHealthy && !([self.handleCloudContentAlert.alertView isVisible] || [self.fixCloudContentAlert.alertView isVisible]))
dispatch_async( dispatch_get_main_queue(), ^{
[self.storeLoading cancelOverlayAnimated:YES];
[self showCloudContentAlert];
} );
if (manager.cloudEnabled && !storeHealthy && !([self.handleCloudContentAlert.alertView isVisible] || [self.fixCloudContentAlert.alertView isVisible])) {
[self.storeLoading cancelOverlayAnimated:YES];
[self.handleCloudDisabledAlert cancelAlertAnimated:YES];
[self showCloudContentAlert];
};
return NO;
}
- (BOOL)ubiquityStoreManagerHandleCloudDisabled:(UbiquityStoreManager *)manager {
if (![self.handleCloudDisabledAlert isVisible])
self.handleCloudDisabledAlert = [PearlAlert showAlertWithTitle:@"iCloud Login" message:
@"You haven't added an iCloud account to your device yet.\n"
@"To add one, tap 'Wait For Me', go into Apple's Settings and add an iCloud account."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == alert.firstOtherButtonIndex)
return;
[self.storeManager reloadStore];
} cancelTitle:@"Wait For Me" otherTitles:@"Disable iCloud", nil];
return YES;
}
- (void)showCloudContentAlert {
__weak MPiOSAppDelegate *wSelf = self;

View File

@ -71,9 +71,10 @@
</dict>
<key>UIAppFonts</key>
<array>
<string>Exo-Bold.otf</string>
<string>Exo-ExtraBold.otf</string>
<string>Exo-Regular.otf</string>
<string>Exo2.0-Bold.otf</string>
<string>Exo2.0-ExtraBold.otf</string>
<string>Exo2.0-Regular.otf</string>
<string>Exo2.0-Thin.otf</string>
<string>SourceCodePro-Black.otf</string>
<string>SourceCodePro-ExtraLight.otf</string>
</array>

View File

@ -12,27 +12,34 @@
93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; };
93D393543ACC701C018C74DA /* PearlUIView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393676C32D23A47E27957 /* PearlUIView.m */; };
93D394F6D3F6E2553AA0D684 /* MPPasswordStoredCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3947F6BB69CA9A9124A5D /* MPPasswordStoredCell.m */; };
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BAA71DE51B4D8A1286C /* MPCell.m */; };
93D393BA1B8402D08DB40231 /* MPPasswordElementCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39342E5F115EFCC90E976 /* MPPasswordElementCell.m */; };
93D394F6D3F6E2553AA0D684 /* MPPasswordLargeStoredCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3947F6BB69CA9A9124A5D /* MPPasswordLargeStoredCell.m */; };
93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */; };
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; };
93D3959643EACF286D0152BA /* PearlUINavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DDDAC305E8ABB4220C7 /* PearlUINavigationBar.m */; };
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3956915634581E737B38C /* PearlNavigationController.m */; };
93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3942A356B639724157982 /* PearlOverlay.h */; };
93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */; };
93D3980046016EFD05B35BC5 /* PearlUICollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */; };
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 */; };
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; };
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; };
93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */; };
93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; };
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39C8AD8EAB747856B3A8C /* LLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3923B42DA2DA18F287092 /* LLModel.m */; };
93D39CB5E2EC1078E898F46A /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937863061C3916AF7AD2 /* MPPasswordCell.m */; };
93D39CB5E2EC1078E898F46A /* MPPasswordLargeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937863061C3916AF7AD2 /* MPPasswordLargeCell.m */; };
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39EDD960C381D64E4DCDD /* MPPasswordSmallCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3952CC60991B97D69F26A /* MPPasswordSmallCell.m */; };
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A28369954D147E239BA /* MPSetupViewController.m */; };
93D39FA97F4C3F69A75D5A03 /* MPPasswordGeneratedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3993422E207BF0B21D089 /* MPPasswordGeneratedCell.m */; };
93D39FA97F4C3F69A75D5A03 /* MPPasswordLargeGeneratedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3993422E207BF0B21D089 /* MPPasswordLargeGeneratedCell.m */; };
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */; };
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */; };
DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; };
@ -69,11 +76,13 @@
DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA6701DF16406BB400B61001 /* AdSupport.framework */; settings = {ATTRIBUTES = (Required, ); }; };
DA672D2F14F92C6B004A189C /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DA672D2E14F92C6B004A189C /* libz.dylib */; };
DA672D3014F9413D004A189C /* libPearl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC77CAD148291A600BCF976 /* libPearl.a */; };
DA67460D18DE7F0C00DFE240 /* Exo2.0-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA67460918DE7F0C00DFE240 /* Exo2.0-Thin.otf */; };
DA67460E18DE7F0C00DFE240 /* Exo2.0-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA67460A18DE7F0C00DFE240 /* Exo2.0-Regular.otf */; };
DA67460F18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA67460B18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf */; };
DA67461018DE7F0C00DFE240 /* Exo2.0-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */; };
DA69540617D975D900BF294E /* icon_gears.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37841711E29500CF925C /* icon_gears.png */; };
DA69540717D975D900BF294E /* icon_gears@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37851711E29500CF925C /* icon_gears@2x.png */; };
DA70EC801811B13C00F65DB2 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */; };
DA829E52159847E0002417D3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DA829E6215984832002417D3 /* libFontReplacer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA829E51159847E0002417D3 /* libFontReplacer.a */; };
DA854C8318D4CFBF00106317 /* avatar-add@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA854C8118D4CFBF00106317 /* avatar-add@2x.png */; };
DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */ = {isa = PBXBuildFile; fileRef = DA854C8218D4CFBF00106317 /* avatar-add.png */; };
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA945C8617E3F3FD0053236B /* Images.xcassets */; };
@ -147,9 +156,6 @@
DABD395C1711E29700CF925C /* avatar-9@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36911711E29400CF925C /* avatar-9@2x.png */; };
DABD395D1711E29700CF925C /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36931711E29400CF925C /* background.png */; };
DABD395E1711E29700CF925C /* background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36941711E29400CF925C /* background@2x.png */; };
DABD39841711E29700CF925C /* Exo-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DABD36BC1711E29500CF925C /* Exo-Bold.otf */; };
DABD39851711E29700CF925C /* Exo-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DABD36BD1711E29500CF925C /* Exo-ExtraBold.otf */; };
DABD39861711E29700CF925C /* Exo-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DABD36BE1711E29500CF925C /* Exo-Regular.otf */; };
DABD39871711E29700CF925C /* SourceCodePro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = DABD36BF1711E29500CF925C /* SourceCodePro-Black.otf */; };
DABD39881711E29700CF925C /* SourceCodePro-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = DABD36C01711E29500CF925C /* SourceCodePro-ExtraLight.otf */; };
DABD39A01711E29700CF925C /* icon_action.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD36DA1711E29500CF925C /* icon_action.png */; };
@ -243,8 +249,6 @@
DAC6326D148680650075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DAC632891486D9690075AEA5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC632871486D95D0075AEA5 /* Security.framework */; };
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DACA22161705DE13002C6C22 /* UIFont+Replacement.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA22141705DE13002C6C22 /* UIFont+Replacement.m */; };
DACA22171705DE13002C6C22 /* UIFont+Replacement.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA22151705DE13002C6C22 /* UIFont+Replacement.h */; };
DACA22191705DE28002C6C22 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DACA22181705DE28002C6C22 /* Crashlytics.framework */; };
DACA22BB1705DE7D002C6C22 /* UbiquityStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */; };
DACA22BC1705DE7D002C6C22 /* NSError+UbiquityStoreManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */; };
@ -373,6 +377,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 */; };
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 */; };
DAFC5656172C573B00CB5CC5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DAFC5683172C57EC00CB5CC5 /* IASKAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5665172C57EC00CB5CC5 /* IASKAppSettingsViewController.m */; };
DAFC5684172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5667172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m */; };
@ -502,46 +510,56 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = "<group>"; };
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
93D39083C93D90C4B94541AD /* PearlUIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUIView.h; sourceTree = "<group>"; };
93D39097C0AAE62C1C321BFC /* MPPasswordTypesCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordTypesCell.m; sourceTree = "<group>"; };
93D390A66F69AB1CDB0BFF93 /* LLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLModel.h; sourceTree = "<group>"; };
93D390EEC85E94D9C888643F /* PearlUINavigationBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUINavigationBar.h; sourceTree = "<group>"; };
93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = "<group>"; };
93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlAdjustInsets.m"; sourceTree = "<group>"; };
93D391243F64A77798B4D6A4 /* MPPasswordTypesCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordTypesCell.h; sourceTree = "<group>"; };
93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsViewController.h; sourceTree = "<group>"; };
93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; };
93D3923B42DA2DA18F287092 /* LLModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLModel.m; sourceTree = "<group>"; };
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; };
93D3932D6C25F2C2D929F8A1 /* MPPasswordElementCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordElementCell.h; sourceTree = "<group>"; };
93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = "<group>"; };
93D393676C32D23A47E27957 /* PearlUIView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUIView.m; sourceTree = "<group>"; };
93D3937863061C3916AF7AD2 /* MPPasswordCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordCell.m; sourceTree = "<group>"; };
93D39342E5F115EFCC90E976 /* MPPasswordElementCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordElementCell.m; sourceTree = "<group>"; };
93D3937863061C3916AF7AD2 /* MPPasswordLargeCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeCell.m; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; };
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 = "<group>"; };
93D3947F6BB69CA9A9124A5D /* MPPasswordStoredCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordStoredCell.m; sourceTree = "<group>"; };
93D3947F6BB69CA9A9124A5D /* MPPasswordLargeStoredCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeStoredCell.m; sourceTree = "<group>"; };
93D3952CC60991B97D69F26A /* MPPasswordSmallCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordSmallCell.m; sourceTree = "<group>"; };
93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; };
93D395BA6B2CFF5F49A4D25F /* MPPasswordStoredCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordStoredCell.h; sourceTree = "<group>"; };
93D395BA6B2CFF5F49A4D25F /* MPPasswordLargeStoredCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeStoredCell.h; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; };
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSetupViewController.h; sourceTree = "<group>"; };
93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLogsViewController.m; sourceTree = "<group>"; };
93D3983278751A530262F64E /* LLConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLConfig.h; sourceTree = "<group>"; };
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; };
93D39888EE06F06264CC963B /* MPPasswordSmallCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordSmallCell.h; sourceTree = "<group>"; };
93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = "<group>"; };
93D3993422E207BF0B21D089 /* MPPasswordGeneratedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordGeneratedCell.m; sourceTree = "<group>"; };
93D3993422E207BF0B21D089 /* MPPasswordLargeGeneratedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeGeneratedCell.m; sourceTree = "<group>"; };
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; };
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = "<group>"; };
93D39A28369954D147E239BA /* MPSetupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSetupViewController.m; sourceTree = "<group>"; };
93D39A3CC4D8330831FC8CB4 /* LLToggleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLToggleViewController.h; sourceTree = "<group>"; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = "<group>"; };
93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = "<group>"; };
93D39BA6C5CB452973918B7D /* LLButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLButtonView.h; sourceTree = "<group>"; };
93D39BAA71DE51B4D8A1286C /* MPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCell.m; sourceTree = "<group>"; };
93D39BF6BCBDFFE844E7D34C /* LLButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLButtonView.m; sourceTree = "<group>"; };
93D39C8E26B06F01566785B7 /* LLToggleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLToggleViewController.m; sourceTree = "<group>"; };
93D39CE1138FDA4D3D1B847A /* MPPasswordGeneratedCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordGeneratedCell.h; sourceTree = "<group>"; };
93D39CE1138FDA4D3D1B847A /* MPPasswordLargeGeneratedCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeGeneratedCell.h; sourceTree = "<group>"; };
93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCombinedViewController.h; sourceTree = "<group>"; };
93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = "<group>"; };
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAvatarCell.h; sourceTree = "<group>"; };
93D39DDDAC305E8ABB4220C7 /* PearlUINavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUINavigationBar.m; sourceTree = "<group>"; };
93D39E02F69CACAB61C056F8 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; };
93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+PearlAdjustInsets.h"; sourceTree = "<group>"; };
93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordCell.m; sourceTree = "<group>"; };
93D39E02F69CACAB61C056F8 /* MPPasswordLargeCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeCell.h; sourceTree = "<group>"; };
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; };
93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
@ -579,8 +597,11 @@
DA6701DD16406B7300B61001 /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; };
DA6701DF16406BB400B61001 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; };
DA672D2E14F92C6B004A189C /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
DA67460918DE7F0C00DFE240 /* Exo2.0-Thin.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Thin.otf"; sourceTree = "<group>"; };
DA67460A18DE7F0C00DFE240 /* Exo2.0-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Regular.otf"; sourceTree = "<group>"; };
DA67460B18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-ExtraBold.otf"; sourceTree = "<group>"; };
DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Bold.otf"; sourceTree = "<group>"; };
DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
DA829E51159847E0002417D3 /* libFontReplacer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFontReplacer.a; sourceTree = BUILT_PRODUCTS_DIR; };
DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = "<group>"; };
DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = "<group>"; };
DA945C8617E3F3FD0053236B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
@ -722,9 +743,6 @@
DABD36911711E29400CF925C /* avatar-9@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-9@2x.png"; sourceTree = "<group>"; };
DABD36931711E29400CF925C /* background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = background.png; sourceTree = "<group>"; };
DABD36941711E29400CF925C /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@2x.png"; sourceTree = "<group>"; };
DABD36BC1711E29500CF925C /* Exo-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo-Bold.otf"; sourceTree = "<group>"; };
DABD36BD1711E29500CF925C /* Exo-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo-ExtraBold.otf"; sourceTree = "<group>"; };
DABD36BE1711E29500CF925C /* Exo-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo-Regular.otf"; sourceTree = "<group>"; };
DABD36BF1711E29500CF925C /* SourceCodePro-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Black.otf"; sourceTree = "<group>"; };
DABD36C01711E29500CF925C /* SourceCodePro-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-ExtraLight.otf"; sourceTree = "<group>"; };
DABD36DA1711E29500CF925C /* icon_action.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon_action.png; sourceTree = "<group>"; };
@ -1305,8 +1323,6 @@
DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAC77CB1148291A600BCF976 /* Pearl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Pearl-Prefix.pch"; path = "../../MasterPassword/ObjC/Pearl/Pearl-Prefix.pch"; sourceTree = "<group>"; };
DACA22141705DE13002C6C22 /* UIFont+Replacement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIFont+Replacement.m"; sourceTree = "<group>"; };
DACA22151705DE13002C6C22 /* UIFont+Replacement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIFont+Replacement.h"; sourceTree = "<group>"; };
DACA22181705DE28002C6C22 /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
DACA22B71705DE7D002C6C22 /* UbiquityStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UbiquityStoreManager.m; sourceTree = "<group>"; };
DACA22B81705DE7D002C6C22 /* NSError+UbiquityStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+UbiquityStoreManager.h"; sourceTree = "<group>"; };
@ -1439,6 +1455,10 @@
DAEB933118AA537D000490CC /* x509_vfy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x509_vfy.h; sourceTree = "<group>"; };
DAEB933218AA537D000490CC /* x509v3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x509v3.h; sourceTree = "<group>"; };
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 = "<group>"; };
DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUINavigationBar.m; sourceTree = "<group>"; };
DAEC85B318E3DD9A007FC0DF /* PearlUINavigationBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUINavigationBar.h; sourceTree = "<group>"; };
DAEC85B418E3DD9A007FC0DF /* PearlUIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUIView.h; sourceTree = "<group>"; };
DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libInAppSettingsKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAFC5659172C573B00CB5CC5 /* InAppSettingsKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "InAppSettingsKit-Prefix.pch"; sourceTree = "<group>"; };
DAFC565A172C573B00CB5CC5 /* InAppSettingsKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InAppSettingsKit.h; sourceTree = "<group>"; };
@ -1585,7 +1605,6 @@
DA6701E016406BB400B61001 /* AdSupport.framework in Frameworks */,
DA6701DE16406B7300B61001 /* Social.framework in Frameworks */,
DA6701B816406A4100B61001 /* Accounts.framework in Frameworks */,
DA829E6215984832002417D3 /* libFontReplacer.a in Frameworks */,
DA44260A1557D9E40052177D /* libUbiquityStoreManager.a in Frameworks */,
DAD312C21552A22700A3F9ED /* libsqlite3.dylib in Frameworks */,
DAD312BF1552A1BD00A3F9ED /* libLocalytics.a in Frameworks */,
@ -1608,14 +1627,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
DA829E4E159847E0002417D3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DA829E52159847E0002417D3 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC6325A1486805C0075AEA5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -1688,14 +1699,8 @@
DAFC5657172C573B00CB5CC5 /* InAppSettingsKit */,
DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */,
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */,
93D3971FE104BB4052484151 /* MPUsersViewController.h */,
93D393676C32D23A47E27957 /* PearlUIView.m */,
93D39083C93D90C4B94541AD /* PearlUIView.h */,
93D39E02F69CACAB61C056F8 /* MPPasswordCell.h */,
93D3937863061C3916AF7AD2 /* MPPasswordCell.m */,
93D395BA6B2CFF5F49A4D25F /* MPPasswordStoredCell.h */,
93D3947F6BB69CA9A9124A5D /* MPPasswordStoredCell.m */,
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */,
93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */,
);
sourceTree = "<group>";
};
@ -1708,7 +1713,6 @@
DAC6326C148680650075AEA5 /* libjrswizzle.a */,
DAD3127115528CD200A3F9ED /* libLocalytics.a */,
DA4425CB1557BED40052177D /* libUbiquityStoreManager.a */,
DA829E51159847E0002417D3 /* libFontReplacer.a */,
DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */,
DAE1EF2917ED112600BC0086 /* libDCIntrospect.a */,
DADEF4221810D5530052CA3E /* libLoveLyndir.a */,
@ -1965,9 +1969,10 @@
DABD36BB1711E29500CF925C /* Fonts */ = {
isa = PBXGroup;
children = (
DABD36BC1711E29500CF925C /* Exo-Bold.otf */,
DABD36BD1711E29500CF925C /* Exo-ExtraBold.otf */,
DABD36BE1711E29500CF925C /* Exo-Regular.otf */,
DA67460918DE7F0C00DFE240 /* Exo2.0-Thin.otf */,
DA67460A18DE7F0C00DFE240 /* Exo2.0-Regular.otf */,
DA67460B18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf */,
DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */,
DABD36BF1711E29500CF925C /* SourceCodePro-Black.otf */,
DABD36C01711E29500CF925C /* SourceCodePro-ExtraLight.otf */,
);
@ -2552,10 +2557,22 @@
DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */,
93D39B381350802A194BF332 /* MPAvatarCell.m */,
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */,
93D39DDDAC305E8ABB4220C7 /* PearlUINavigationBar.m */,
93D390EEC85E94D9C888643F /* PearlUINavigationBar.h */,
93D3993422E207BF0B21D089 /* MPPasswordGeneratedCell.m */,
93D39CE1138FDA4D3D1B847A /* MPPasswordGeneratedCell.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 */,
93D3952CC60991B97D69F26A /* MPPasswordSmallCell.m */,
93D39888EE06F06264CC963B /* MPPasswordSmallCell.h */,
93D39342E5F115EFCC90E976 /* MPPasswordElementCell.m */,
93D3932D6C25F2C2D929F8A1 /* MPPasswordElementCell.h */,
);
path = iOS;
sourceTree = "<group>";
@ -2581,7 +2598,6 @@
DABF632217B744F900DA7E38 /* GoogleOpenSource.framework */,
DABF632317B744F900DA7E38 /* GooglePlus.framework */,
DACA22181705DE28002C6C22 /* Crashlytics.framework */,
DACA22131705DE13002C6C22 /* UIFont+Replacement */,
DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */,
DACA22C71705DEB0002C6C22 /* Localytics */,
DAC77CAF148291A600BCF976 /* Pearl */,
@ -2592,16 +2608,6 @@
path = ../../../External;
sourceTree = "<group>";
};
DACA22131705DE13002C6C22 /* UIFont+Replacement */ = {
isa = PBXGroup;
children = (
DACA22141705DE13002C6C22 /* UIFont+Replacement.m */,
DACA22151705DE13002C6C22 /* UIFont+Replacement.h */,
);
name = "UIFont+Replacement";
path = "FontReplacer/UIFont+Replacement";
sourceTree = "<group>";
};
DACA22B61705DE7D002C6C22 /* UbiquityStoreManager */ = {
isa = PBXGroup;
children = (
@ -3045,6 +3051,10 @@
DAFE460715039823003ABA7C /* Pearl-UIKit */ = {
isa = PBXGroup;
children = (
DAEC85B118E3DD9A007FC0DF /* PearlUIView.m */,
DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */,
DAEC85B318E3DD9A007FC0DF /* PearlUINavigationBar.h */,
DAEC85B418E3DD9A007FC0DF /* PearlUIView.h */,
DA2CA4E518D2AC10007798F8 /* NSLayoutConstraint+PearlUIKit.m */,
DA2CA4E218D28866007798F8 /* NSLayoutConstraint+PearlUIKit.h */,
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */,
@ -3094,6 +3104,10 @@
93D3942A356B639724157982 /* PearlOverlay.h */,
93D3956915634581E737B38C /* PearlNavigationController.m */,
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */,
93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */,
93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */,
93D39D8A953779B35403AF6E /* PearlUICollectionView.m */,
93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */,
);
path = "Pearl-UIKit";
sourceTree = "<group>";
@ -3118,14 +3132,6 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
DA829E4F159847E0002417D3 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
DACA22171705DE13002C6C22 /* UIFont+Replacement.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DAC6325B1486805C0075AEA5 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
@ -3207,6 +3213,7 @@
DAEB933C18AA537D000490CC /* aes.h in Headers */,
DAEB937618AA537D000490CC /* ssl2.h in Headers */,
DAFE4A3C15039824003ABA7C /* Pearl-UIKit-Dependencies.h in Headers */,
DAEC85B818E3DD9A007FC0DF /* PearlUIView.h in Headers */,
DAEB935B18AA537D000490CC /* krb5_asn.h in Headers */,
DAEB935818AA537D000490CC /* evp.h in Headers */,
DAEB934118AA537D000490CC /* blowfish.h in Headers */,
@ -3275,6 +3282,7 @@
DAEB935C18AA537D000490CC /* kssl.h in Headers */,
DAEB933418AA537D000490CC /* crypto_scrypt.h in Headers */,
DA3509FE15F101A500C14A8E /* PearlQueue.h in Headers */,
DAEC85B718E3DD9A007FC0DF /* PearlUINavigationBar.h in Headers */,
DAEB934918AA537D000490CC /* conf_api.h in Headers */,
93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */,
DAEB937518AA537D000490CC /* ssl.h in Headers */,
@ -3282,6 +3290,8 @@
93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */,
DAEB936218AA537D000490CC /* obj_mac.h in Headers */,
DAEB934218AA537D000490CC /* bn.h in Headers */,
93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */,
93D3980046016EFD05B35BC5 /* PearlUICollectionView.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -3324,6 +3334,7 @@
DA5BFA40147E415C00F98B1E /* Sources */,
DA5BFA41147E415C00F98B1E /* Frameworks */,
DA5BFA42147E415C00F98B1E /* Resources */,
DA67460818DE7B2C00DFE240 /* Run Script: Moarfonts */,
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */,
);
@ -3336,23 +3347,6 @@
productReference = DA5BFA44147E415C00F98B1E /* MasterPassword.app */;
productType = "com.apple.product-type.application";
};
DA829E50159847E0002417D3 /* FontReplacer */ = {
isa = PBXNativeTarget;
buildConfigurationList = DA829E59159847E0002417D3 /* Build configuration list for PBXNativeTarget "FontReplacer" */;
buildPhases = (
DA829E4D159847E0002417D3 /* Sources */,
DA829E4E159847E0002417D3 /* Frameworks */,
DA829E4F159847E0002417D3 /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = FontReplacer;
productName = FontReplacer;
productReference = DA829E51159847E0002417D3 /* libFontReplacer.a */;
productType = "com.apple.product-type.library.static";
};
DAC6325C1486805C0075AEA5 /* uicolor-utilities */ = {
isa = PBXNativeTarget;
buildConfigurationList = DAC632651486805C0075AEA5 /* Build configuration list for PBXNativeTarget "uicolor-utilities" */;
@ -3597,7 +3591,6 @@
DAC6326B148680650075AEA5 /* jrswizzle */,
DAD3127015528CD200A3F9ED /* Localytics */,
DA4425CA1557BED40052177D /* UbiquityStoreManager */,
DA829E50159847E0002417D3 /* FontReplacer */,
DAFC5654172C573B00CB5CC5 /* InAppSettingsKit */,
DAE1EF2817ED112600BC0086 /* DCIntrospect */,
DADEF4211810D5530052CA3E /* LoveLyndir */,
@ -3642,6 +3635,7 @@
DABD391D1711E29700CF925C /* ui_spinner.png in Resources */,
DABD391E1711E29700CF925C /* ui_spinner@2x.png in Resources */,
DA69540617D975D900BF294E /* icon_gears.png in Resources */,
DA67460D18DE7F0C00DFE240 /* Exo2.0-Thin.otf in Resources */,
DABD39271711E29700CF925C /* ui_textfield.png in Resources */,
DABD39281711E29700CF925C /* ui_textfield@2x.png in Resources */,
DABD39291711E29700CF925C /* ui_toolbar_container.png in Resources */,
@ -3656,6 +3650,7 @@
DABD393D1711E29700CF925C /* avatar-11@2x.png in Resources */,
DABD393E1711E29700CF925C /* avatar-12.png in Resources */,
DABD393F1711E29700CF925C /* avatar-12@2x.png in Resources */,
DA67461018DE7F0C00DFE240 /* Exo2.0-Bold.otf in Resources */,
DABD39401711E29700CF925C /* avatar-13.png in Resources */,
DABD39411711E29700CF925C /* avatar-13@2x.png in Resources */,
DABD39421711E29700CF925C /* avatar-14.png in Resources */,
@ -3670,9 +3665,11 @@
DABD394A1711E29700CF925C /* avatar-18.png in Resources */,
DABD394B1711E29700CF925C /* avatar-18@2x.png in Resources */,
DABD394C1711E29700CF925C /* avatar-1@2x.png in Resources */,
DA67460E18DE7F0C00DFE240 /* Exo2.0-Regular.otf in Resources */,
DABD394D1711E29700CF925C /* avatar-2.png in Resources */,
DABD394E1711E29700CF925C /* avatar-2@2x.png in Resources */,
DABD394F1711E29700CF925C /* avatar-3.png in Resources */,
DA67460F18DE7F0C00DFE240 /* Exo2.0-ExtraBold.otf in Resources */,
DABD39501711E29700CF925C /* avatar-3@2x.png in Resources */,
DABD39511711E29700CF925C /* avatar-4.png in Resources */,
DABD39521711E29700CF925C /* avatar-4@2x.png in Resources */,
@ -3689,9 +3686,6 @@
DABD395C1711E29700CF925C /* avatar-9@2x.png in Resources */,
DABD395D1711E29700CF925C /* background.png in Resources */,
DABD395E1711E29700CF925C /* background@2x.png in Resources */,
DABD39841711E29700CF925C /* Exo-Bold.otf in Resources */,
DABD39851711E29700CF925C /* Exo-ExtraBold.otf in Resources */,
DABD39861711E29700CF925C /* Exo-Regular.otf in Resources */,
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */,
DABD39871711E29700CF925C /* SourceCodePro-Black.otf in Resources */,
DADEF4161810D2940052CA3E /* love-lyndir.button.red.png in Resources */,
@ -3794,6 +3788,21 @@
shellPath = "/bin/bash -e";
shellScript = "PATH+=:/usr/libexec\n\naddPlistWithKey() {\n local key=$1 type=$2 value=$3 plist=${4:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Delete :'$key'\" \"$plist\" 2>/dev/null || true\n PlistBuddy -c \"Add :'$key' '$type' '$value'\" \"$plist\"\n}\nsetPlistWithKey() {\n local key=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Set :'$key' '$value'\" \"$plist\"\n}\ngetPlistWithKey() {\n local key=$1 plist=${2:-\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"}\n \n PlistBuddy -c \"Print :'$key'\" \"$plist\"\n}\nsetSettingWithTitle() {\n local i title=$1 value=$2 plist=${3:-\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Settings.bundle/Root.plist\"}\n \n for (( i=0; 1; ++i )); do\n PlistBuddy -c \"Print :PreferenceSpecifiers:$i\" \"$plist\" &>/dev/null || break\n echo \"Checking preference specifier $i\"\n \n [[ $(PlistBuddy -c \"Print :PreferenceSpecifiers:$i:Title\" \"$plist\" 2>/dev/null) = $title ]] || continue\n \n echo \"Correct title, setting value.\"\n PlistBuddy -c \"Set :PreferenceSpecifiers:$i:DefaultValue $value\" \"$plist\"\n break\n done\n}\n\ndescription=$(git describe --always --dirty --long)\nversion=${description%-g*}\nIFS=- read major minor <<< \"$version\"\nprintf -v version '%s.%02d' \"$major\" \"$minor\"\nprintf -v commit '%09d' \"$((16#${description##*-g}))\"\n\naddPlistWithKey GITDescription string \"$description\"\nsetPlistWithKey CFBundleVersion \"${version//.}$commit\" # No separator between version and commit because I had already submitted a CFBundleVersion with a really high major. Cry.\nsetPlistWithKey CFBundleShortVersionString \"$version\"\n\nsetSettingWithTitle \"Build\" \"$commit\"\nsetSettingWithTitle \"Version\" \"$version\"\nsetSettingWithTitle \"Copyright\" \"$(getPlistWithKey NSHumanReadableCopyright)\"\n\nif [[ $DEPLOYMENT_LOCATION = YES ]]; then\n # This build is a release. Do some release checks.\n passed=1\n [[ $description != *-dirty ]] || \\\n { passed=0; echo >&2 \"ERROR: Cannot release a dirty version, first commit any changes.\"; }\n [[ $(PlistBuddy -c \"Print :'API Key'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Crashlytics.plist\") ]] || \\\n { passed=0; echo >&2 \"ERROR: Cannot release: Crashlytics API key is missing.\"; }\n [[ $(PlistBuddy -c \"Print :'ClientID'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Google+.plist\") ]] || \\\n { passed=0; echo >&2 \"ERROR: Cannot release: Google+ ClientID is missing.\"; }\n [[ $(PlistBuddy -c \"Print :'Key.distribution'\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Localytics.plist\") ]] || \\\n { passed=0; echo >&2 \"ERROR: Cannot release: Localytics distribution key is missing.\"; }\n (( passed )) || \\\n { echo >&2 \"Failed to pass release checks. Fix the above errors and re-try. Aborting.\"; exit 1; }\nfi";
};
DA67460818DE7B2C00DFE240 /* Run Script: Moarfonts */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script: Moarfonts";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = "/bin/bash -e";
shellScript = "[[ -x /usr/local/bin/moarfonts ]] || {\n echo >&2 \"moarfonts not installed, embedded fonts will not show up in IB.\"\n exit\n}\n\nfind \"${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}\" -name '*.otf' -exec /usr/local/bin/moarfonts install {} +";
showEnvVarsInLog = 0;
};
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -3858,19 +3867,14 @@
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */,
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */,
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */,
93D3959643EACF286D0152BA /* PearlUINavigationBar.m in Sources */,
93D393543ACC701C018C74DA /* PearlUIView.m in Sources */,
93D39CB5E2EC1078E898F46A /* MPPasswordCell.m in Sources */,
93D39FA97F4C3F69A75D5A03 /* MPPasswordGeneratedCell.m in Sources */,
93D394F6D3F6E2553AA0D684 /* MPPasswordStoredCell.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DA829E4D159847E0002417D3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DACA22161705DE13002C6C22 /* UIFont+Replacement.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 */,
93D39EDD960C381D64E4DCDD /* MPPasswordSmallCell.m in Sources */,
93D393BA1B8402D08DB40231 /* MPPasswordElementCell.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -3924,6 +3928,7 @@
DAFE4A5115039824003ABA7C /* PearlUIDebug.m in Sources */,
DAFE4A5315039824003ABA7C /* PearlUIUtils.m in Sources */,
DAFE4A5515039824003ABA7C /* PearlValidatingTextField.m in Sources */,
DAEC85B618E3DD9A007FC0DF /* PearlUINavigationBar.m in Sources */,
DAFE4A5715039824003ABA7C /* PearlWebViewController.m in Sources */,
DAFE4A5915039824003ABA7C /* UIImage+PearlScaling.m in Sources */,
DAFE4A62150399FF003ABA7C /* PearlAppDelegate.m in Sources */,
@ -3934,6 +3939,7 @@
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */,
DAFE4A63150399FF003ABA82 /* UIControl+PearlBlocks.m in Sources */,
DAFE4A63150399FF003ABA86 /* NSObject+PearlKVO.m in Sources */,
DAEC85B518E3DD9A007FC0DF /* PearlUIView.m in Sources */,
DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */,
DAFE4A63150399FF003ABA8A /* UIControl+PearlSelect.m in Sources */,
DAFE4A63150399FF003ABA8E /* UIScrollView+PearlFlashingIndicators.m in Sources */,
@ -3947,6 +3953,8 @@
93D396AA30690B256F30378A /* PearlNavigationController.m in Sources */,
93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */,
DA2CA4DF18D28859007798F8 /* NSTimer+PearlBlock.m in Sources */,
93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */,
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -4244,6 +4252,7 @@
EXCLUDED_SOURCE_FILE_NAMES = libTestFlight.a;
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
@ -4269,6 +4278,7 @@
EXCLUDED_SOURCE_FILE_NAMES = "";
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "4CBD21E7-DB60-4F7F-80F8-98DFA83D2CE0";
SKIP_INSTALL = NO;
@ -4277,27 +4287,6 @@
};
name = "AdHoc-iOS";
};
DA829E5A159847E0002417D3 /* Debug-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
};
name = "Debug-iOS";
};
DA829E5B159847E0002417D3 /* AdHoc-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
};
name = "AdHoc-iOS";
};
DA829E5C159847E0002417D3 /* AppStore-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
};
name = "AppStore-iOS";
};
DA95D60914DF3F3B008D1B94 /* AppStore-iOS */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -4405,6 +4394,7 @@
);
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
INFOPLIST_FILE = "MasterPassword-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "6C6B84DD-9D8F-4321-BD77-5F737DBE1778";
SKIP_INSTALL = NO;
@ -4677,16 +4667,6 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "AdHoc-iOS";
};
DA829E59159847E0002417D3 /* Build configuration list for PBXNativeTarget "FontReplacer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DA829E5A159847E0002417D3 /* Debug-iOS */,
DA829E5B159847E0002417D3 /* AdHoc-iOS */,
DA829E5C159847E0002417D3 /* AppStore-iOS */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "AdHoc-iOS";
};
DAC632651486805C0075AEA5 /* Build configuration list for PBXNativeTarget "uicolor-utilities" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@ -1,43 +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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// PearlUINavigationBar.h
// PearlUINavigationBar
//
// Created by lhunath on 2014-03-17.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "PearlUINavigationBar.h"
@implementation PearlUINavigationBar
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (self.ignoreTouches && hitView == self)
return nil;
return hitView;
}
- (void)setInvisible:(BOOL)invisible {
_invisible = invisible;
if (invisible) {
self.translucent = YES;
self.shadowImage = [UIImage new];
[self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
}
}
@end

View File

@ -1,32 +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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// PearlUIView.h
// PearlUIView
//
// Created by lhunath on 2014-03-17.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "PearlUIView.h"
@implementation PearlUIView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (self.ignoreTouches && hitView == self)
return nil;
return hitView;
}
@end

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.