diff --git a/External/Pearl b/External/Pearl
index 0e9484b6..daa260b3 160000
--- a/External/Pearl
+++ b/External/Pearl
@@ -1 +1 @@
-Subproject commit 0e9484b6512150fbbfffdddcdec62f8e9a741254
+Subproject commit daa260b3ecaca266ffcfa597e9350900dda6cc56
diff --git a/External/iOS/Crashlytics.framework/Versions/A/Crashlytics b/External/iOS/Crashlytics.framework/Versions/A/Crashlytics
index f5c38d4f..f286bd15 100644
Binary files a/External/iOS/Crashlytics.framework/Versions/A/Crashlytics and b/External/iOS/Crashlytics.framework/Versions/A/Crashlytics differ
diff --git a/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist b/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
index 4971ea17..608be399 100644
--- a/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
+++ b/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
@@ -15,13 +15,13 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.1.9
+ 2.2.0
CFBundleSupportedPlatforms
iPhoneOS
CFBundleVersion
- 31
+ 33
DTPlatformName
iphoneos
MinimumOSVersion
diff --git a/External/iOS/Crashlytics.framework/run b/External/iOS/Crashlytics.framework/run
index 599d7616..d3ddf05f 100755
Binary files a/External/iOS/Crashlytics.framework/run and b/External/iOS/Crashlytics.framework/run differ
diff --git a/External/iOS/Crashlytics.framework/submit b/External/iOS/Crashlytics.framework/submit
new file mode 100755
index 00000000..2a5784eb
Binary files /dev/null and b/External/iOS/Crashlytics.framework/submit differ
diff --git a/MasterPassword/ObjC/MPAppDelegate_Key.m b/MasterPassword/ObjC/MPAppDelegate_Key.m
index 9aa55c10..f8fe462d 100644
--- a/MasterPassword/ObjC/MPAppDelegate_Key.m
+++ b/MasterPassword/ObjC/MPAppDelegate_Key.m
@@ -107,13 +107,11 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
}
// Method 3: Check the given master password string.
- if (!tryKey) {
- if ([password length]) if ((tryKey = [MPAlgorithmDefault keyForPassword:password
- ofUserNamed:user.name])) if (![user.keyID isEqual:tryKey.keyID]) {
- inf( @"Key derived from password doesn't match keyID for: %@", user.userID );
+ if (!tryKey && [password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name]) &&
+ ![user.keyID isEqual:tryKey.keyID]) {
+ inf( @"Key derived from password doesn't match keyID for: %@", user.userID );
- tryKey = nil;
- }
+ tryKey = nil;
}
// No more methods left, fail if key still not known.
diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m b/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m
index 0f9cedb3..654ba6ee 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m
+++ b/MasterPassword/ObjC/iOS/MPPasswordLargeCell.m
@@ -30,6 +30,8 @@
+ (instancetype)dequeueCellWithType:(MPElementType)type fromCollectionView:(UICollectionView *)collectionView
atIndexPath:(NSIndexPath *)indexPath {
+ NSAssert(type != 0 && type != (MPElementType)NSNotFound, @"Cannot dequeue a password cell without a type.");
+
NSString *reuseIdentifier;
if (type & MPElementTypeClassGenerated)
reuseIdentifier = NSStringFromClass( [MPPasswordLargeGeneratedCell class] );
@@ -163,6 +165,13 @@
return YES;
}
+- (void)textFieldDidBeginEditing:(UITextField *)textField {
+
+ UICollectionView *collectionView = [UICollectionView findAsSuperviewOf:self];
+ [collectionView scrollToItemAtIndexPath:[collectionView indexPathForCell:self]
+ atScrollPosition:UICollectionViewScrollPositionTop animated:YES];
+}
+
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField == self.contentField) {
diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.h b/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.h
index 03add62f..93de5523 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.h
+++ b/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.h
@@ -21,4 +21,5 @@
@interface MPPasswordLargeDeleteCell : MPPasswordLargeCell
++ (MPPasswordLargeCell *)dequeueCellFromCollectionView:(UICollectionView *)view atIndexPath:(NSIndexPath *)path;
@end
diff --git a/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.m b/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.m
index a422f54a..05db27a5 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.m
+++ b/MasterPassword/ObjC/iOS/MPPasswordLargeDeleteCell.m
@@ -1,12 +1,12 @@
/**
- * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
- *
- * See the enclosed file LICENSE for license information (LGPLv3). If you did
- * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
- *
- * @author Maarten Billemont
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt
- */
+* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+*
+* See the enclosed file LICENSE for license information (LGPLv3). If you did
+* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+*
+* @author Maarten Billemont
+* @license http://www.gnu.org/licenses/lgpl-3.0.txt
+*/
//
// MPPasswordLargeDeleteCell.h
@@ -22,6 +22,15 @@
#pragma mark - Lifecycle
++ (MPPasswordLargeCell *)dequeueCellFromCollectionView:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)indexPath {
+
+ MPPasswordLargeCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass( [self class] )
+ forIndexPath:indexPath];
+ cell.type = (MPElementType)NSNotFound;
+
+ return cell;
+}
+
- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context {
return element;
diff --git a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m b/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m
index c316d21d..a567ad63 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m
+++ b/MasterPassword/ObjC/iOS/MPPasswordTypesCell.m
@@ -87,30 +87,32 @@
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (!self.algorithm)
- dbg_return_tr( 0, @, @(section) );
+ return 0;
if (self.transientSite)
- dbg_return_tr( [[self.algorithm allTypes] count], @, @(section) );
+ return [[self.algorithm allTypes] count];
- dbg_return_tr( [[self.algorithm allTypes] count] + 1 /* Delete */, @, @(section) );
+ return [[self.algorithm allTypes] count] + 1 /* Delete */;
}
- (MPPasswordLargeCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MPPasswordLargeCell *cell;
if (indexPath.item == 0)
- cell = [MPPasswordLargeDeleteCell dequeueCellWithType:(MPElementType)NSNotFound fromCollectionView:collectionView
- atIndexPath:indexPath];
+ cell = [MPPasswordLargeDeleteCell dequeueCellFromCollectionView:collectionView atIndexPath:indexPath];
else
cell = [MPPasswordLargeCell dequeueCellWithType:[self typeForContentIndexPath:indexPath] fromCollectionView:collectionView
atIndexPath:indexPath];
+ [cell prepareForReuse];
+
if (self.transientSite)
[cell updateWithTransientSite:self.transientSite];
else
[cell updateWithElement:self.mainElement];
+ dbg( @"cell %d, contentFieldMode: %d", indexPath.item, cell.contentFieldMode );
- dbg_return( cell, indexPath );
+ return cell;
}
#pragma mark - UICollectionViewDelegateFlowLayout
diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.m b/MasterPassword/ObjC/iOS/MPUsersViewController.m
index 9e0a9d3a..eae97458 100644
--- a/MasterPassword/ObjC/iOS/MPUsersViewController.m
+++ b/MasterPassword/ObjC/iOS/MPUsersViewController.m
@@ -129,7 +129,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
break;
}
case MPActiveUserStateLogin: {
- [self.entryField endEditing:YES];
+ self.entryField.enabled = NO;
[self selectedAvatar].spinnerActive = YES;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
BOOL signedIn = NO, isNew = NO;
@@ -140,6 +140,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.entryField.text = @"";
+ self.entryField.enabled = YES;
[self selectedAvatar].spinnerActive = NO;
if (!signedIn) {
@@ -189,7 +190,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
return NO;
}
- [self.entryField endEditing:YES];
+ self.entryField.enabled = NO;
MPAvatarCell *avatarCell = [self selectedAvatar];
avatarCell.spinnerActive = YES;
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
@@ -204,6 +205,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context usingMasterPassword:masterPassword];
PearlMainQueue( ^{
self.entryField.text = @"";
+ self.entryField.enabled = YES;
[self selectedAvatar].spinnerActive = NO;
if (!signedIn) {
@@ -315,15 +317,29 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
}
BOOL isNew = NO;
- MPUserEntity *user = [self userForIndexPath:indexPath inContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
- isNew:&isNew];
+ NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
+ MPUserEntity *mainUser = [self userForIndexPath:indexPath inContext:mainContext isNew:&isNew];
if (isNew)
self.activeUserState = MPActiveUserStateUserName;
- else if (!user.keyID)
+ else if (!mainUser.keyID)
self.activeUserState = MPActiveUserStateMasterPasswordChoice;
- else
+ else {
self.activeUserState = MPActiveUserStateLogin;
+
+ self.entryField.enabled = NO;
+ [self selectedAvatar].spinnerActive = YES;
+ BOOL signedIn = NO;
+ if (!isNew && mainUser)
+ signedIn = [[MPiOSAppDelegate get] signInAsUser:mainUser saveInContext:mainContext usingMasterPassword:nil];
+
+ self.entryField.text = @"";
+ self.entryField.enabled = YES;
+ [self selectedAvatar].spinnerActive = NO;
+
+ if (!signedIn)
+ [self.entryField becomeFirstResponder];
+ }
}
}
diff --git a/MasterPassword/ObjC/iOS/Storyboard.storyboard b/MasterPassword/ObjC/iOS/Storyboard.storyboard
index 301a77d6..63b4c275 100644
--- a/MasterPassword/ObjC/iOS/Storyboard.storyboard
+++ b/MasterPassword/ObjC/iOS/Storyboard.storyboard
@@ -16,19 +16,19 @@
-
+
-
+
-
+
-
+
@@ -39,7 +39,7 @@
-
+
@@ -110,11 +110,11 @@
-
+
-
+
@@ -123,7 +123,7 @@
-
+
-
+
+
@@ -692,7 +695,7 @@
-
+
-
+
@@ -1774,7 +1777,7 @@
-
+
@@ -1792,7 +1795,7 @@
-
+
@@ -1942,22 +1945,47 @@
-
@@ -2565,14 +2593,14 @@ to change type or delete them
-
+
-
+
-
+
@@ -2580,19 +2608,20 @@ to change type or delete them
-
+
.
-This means your passwords are safe from loss and theft: if you ever lose your phone or user, just recreate the user with the same master password, add your sites back and their original passwords will re-appear.
+
+This is very different from most any other password app: when your passwords are not stored, they are safe from loss and theft if you lose your phone or user. On your new phone, just recreate the user with the same master password and your sites will generate their original passwords again. The cryptography used by this app is also much stronger than that used by most other password applications.
You don't need to remember any password generated by this app, but
-
+
@@ -2600,7 +2629,7 @@ You don't need to remember any password generated by this app, but
-
+
@@ -2608,20 +2637,22 @@ You don't need to remember any password generated by this app, but
-
+
and that it is secure.
-Don't reuse an old password!
-Never give out your master password; not even to family. Share site passwords instead.
-A small sentence is a great password.
+ – Don't reuse an old password!
+ – Never share your master password; not even with family.
+ Share your site’s password instead.
+ – A small sentence is a great password.
+
-
+
@@ -2629,18 +2660,37 @@ A small sentence is a great password.
-
+
-You can make passwords for anything, like email addresses, sites or real-world things like a bike lock: if you can name it, you can get a password for it.
+You can make passwords for anything, like email addresses, sites or real-world things like a bike lock: if you can name it, you can get a password for it.
+
+See
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2712,4 +2762,7 @@ You can make passwords for anything, like email addresses, sites or real-world t
+
+
+