From d7d91d13c6d3259df306af201a935b5e60158731 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Fri, 8 Feb 2013 18:11:24 -0500 Subject: [PATCH] UI updates: site search & type change [ADDED] Display the password type when searching for sites. [ADDED] Indicate the strength of a password type by time to crack. [ADDED] Display a password prediction below each password type on the type selection screen. [MOVED] Moved the Short type below the Basic type because it's less secure. [FIXED] Some font regression bugs. --- MasterPassword-iOS.xcodeproj/project.pbxproj | 4 + .../MasterPassword (Development).xcscheme | 4 +- .../lhunath/masterpassword/MPElementType.java | 2 +- MasterPassword/MPAlgorithmV0.m | 16 +- MasterPassword/MPAppDelegate_Store.h | 3 +- MasterPassword/MPAppDelegate_Store.m | 17 +- MasterPassword/MPTypes.h | 2 +- MasterPassword/iOS/MPAllSitesViewController.m | 8 +- MasterPassword/iOS/MPMainViewController.m | 11 +- MasterPassword/iOS/MPSearchDelegate.m | 9 +- MasterPassword/iOS/MPTypeViewController.h | 7 +- MasterPassword/iOS/MPTypeViewController.m | 37 +++- MasterPassword/iOS/MPUnlockViewController.m | 2 +- .../iOS/MainStoryboard_iPhone.storyboard | 158 +++++++++++++----- MasterPassword/iOS/MasterPassword-Info.plist | 1 + Resources/Fonts/Exo-Bold.otf | Bin 0 -> 111716 bytes Resources/ciphers.plist | 8 +- 17 files changed, 208 insertions(+), 81 deletions(-) create mode 100644 Resources/Fonts/Exo-Bold.otf diff --git a/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword-iOS.xcodeproj/project.pbxproj index 248a3586..90cd7d54 100644 --- a/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -754,6 +754,7 @@ DAB8D97C1503718B00CED3BC /* jquery-1.6.1.min.js in Resources */ = {isa = PBXBuildFile; fileRef = DAB8D6AB15036BF600CED3BC /* jquery-1.6.1.min.js */; }; DAB90E9916BD951200D06F4A /* SourceCodePro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAB90E9316BD951200D06F4A /* SourceCodePro-Black.otf */; }; DAB90E9B16BD951200D06F4A /* SourceCodePro-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAB90E9516BD951200D06F4A /* SourceCodePro-ExtraLight.otf */; }; + DAB90EA016BE1B4200D06F4A /* Exo-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAB90E9F16BE1B4200D06F4A /* Exo-Bold.otf */; }; DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; }; DAC4149215C53C48007A716E /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DAC4149115C53C48007A716E /* dictionary.lst */; }; DAC6325E1486805C0075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; @@ -1857,6 +1858,7 @@ DAB8D6F915036BF600CED3BC /* tip_location_wood@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_wood@2x.png"; sourceTree = ""; }; DAB90E9316BD951200D06F4A /* SourceCodePro-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Black.otf"; sourceTree = ""; }; DAB90E9516BD951200D06F4A /* SourceCodePro-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-ExtraLight.otf"; sourceTree = ""; }; + DAB90E9F16BE1B4200D06F4A /* Exo-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo-Bold.otf"; sourceTree = ""; }; DABB980C150FF40100B05417 /* SendToMac-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SendToMac-Prefix.pch"; sourceTree = ""; }; DABB980D150FF40100B05417 /* SendToMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SendToMac.h; sourceTree = ""; }; DABB980E150FF40100B05417 /* SendToMac.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SendToMac.m; sourceTree = ""; }; @@ -2748,6 +2750,7 @@ children = ( DAB90E9316BD951200D06F4A /* SourceCodePro-Black.otf */, DAB90E9516BD951200D06F4A /* SourceCodePro-ExtraLight.otf */, + DAB90E9F16BE1B4200D06F4A /* Exo-Bold.otf */, DAB8D4F915036BF600CED3BC /* Exo-ExtraBold.otf */, DAB8D4FD15036BF600CED3BC /* Exo-Regular.otf */, ); @@ -4570,6 +4573,7 @@ DA692DBF16BB191100F14463 /* Google+.plist in Resources */, DAB90E9916BD951200D06F4A /* SourceCodePro-Black.otf in Resources */, DAB90E9B16BD951200D06F4A /* SourceCodePro-ExtraLight.otf in Resources */, + DAB90EA016BE1B4200D06F4A /* Exo-Bold.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme b/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme index e179af1f..9ac0e8fb 100644 --- a/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme +++ b/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword (Development).xcscheme @@ -50,8 +50,8 @@ + (NSManagedObjectContext *)managedObjectContextForThreadIfReady; -+ (BOOL)managedObjectContextPerform:(void (^)(NSManagedObjectContext *moc))mocBlock; ++ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *moc))mocBlock; ++ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *))mocBlock; - (MPImportResult)importSites:(NSString *)importedSitesString askImportPassword:(NSString *(^)(NSString *userName))importPassword diff --git a/MasterPassword/MPAppDelegate_Store.m b/MasterPassword/MPAppDelegate_Store.m index f27c448b..84b0b79a 100644 --- a/MasterPassword/MPAppDelegate_Store.m +++ b/MasterPassword/MPAppDelegate_Store.m @@ -27,7 +27,7 @@ static char privateManagedObjectContextKey, mainManagedObjectContextKey; return threadManagedObjectContext; } -+ (BOOL)managedObjectContextPerform:(void (^)(NSManagedObjectContext *))mocBlock { ++ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *))mocBlock { NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady]; if (!mainManagedObjectContext) @@ -42,6 +42,21 @@ static char privateManagedObjectContextKey, mainManagedObjectContextKey; return YES; } ++ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *))mocBlock { + + NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady]; + if (!mainManagedObjectContext) + return NO; + + NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; + moc.parentContext = mainManagedObjectContext; + [moc performBlockAndWait:^{ + mocBlock(moc); + }]; + + return YES; +} + - (NSManagedObjectContext *)mainManagedObjectContextIfReady { if (![self privateManagedObjectContextIfReady]) diff --git a/MasterPassword/MPTypes.h b/MasterPassword/MPTypes.h index 74489553..cb72989b 100644 --- a/MasterPassword/MPTypes.h +++ b/MasterPassword/MPTypes.h @@ -32,8 +32,8 @@ typedef enum { MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0, - MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0, + MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0, MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0, MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent, diff --git a/MasterPassword/iOS/MPAllSitesViewController.m b/MasterPassword/iOS/MPAllSitesViewController.m index 9ddddb4d..254d28bf 100644 --- a/MasterPassword/iOS/MPAllSitesViewController.m +++ b/MasterPassword/iOS/MPAllSitesViewController.m @@ -57,10 +57,10 @@ if (![siteName length]) return; - [MPAppDelegate managedObjectContextPerform:^(NSManagedObjectContext *moc) { + [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { MPUserEntity *activeUser = [[MPAppDelegate get] activeUserInContext:moc]; assert(activeUser); - + MPElementType type = activeUser.defaultType; if (!type) type = activeUser.defaultType = MPElementTypeGeneratedLong; @@ -235,8 +235,8 @@ MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = element.name; - cell.detailTextLabel.text = PearlString(@"Used %d times, last on %@", - element.uses, [self.dateFormatter stringFromDate:element.lastUsed]); + cell.detailTextLabel.text = PearlString(@"%d views, last on %@: %@", + element.uses, [self.dateFormatter stringFromDate:element.lastUsed], [element.algorithm shortNameOfType:element.type]); } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m index f8a8792f..6a2b20ae 100644 --- a/MasterPassword/iOS/MPMainViewController.m +++ b/MasterPassword/iOS/MPMainViewController.m @@ -796,12 +796,17 @@ - (MPElementType)selectedType { - __block MPElementType selectedType; + return [self selectedElement].type; +} + +- (MPElementEntity *)selectedElement { + + __block MPElementEntity *selectedElement; [self activeElementDo:^(MPElementEntity *activeElement) { - selectedType = activeElement.type; + selectedElement = activeElement; }]; - return selectedType; + return selectedElement; } - (void)didSelectType:(MPElementType)type { diff --git a/MasterPassword/iOS/MPSearchDelegate.m b/MasterPassword/iOS/MPSearchDelegate.m index 117fae41..e377b702 100644 --- a/MasterPassword/iOS/MPSearchDelegate.m +++ b/MasterPassword/iOS/MPSearchDelegate.m @@ -95,6 +95,7 @@ controller.searchBar.prompt = @"Enter the site's name:"; controller.searchBar.showsScopeBar = controller.searchBar.selectedScopeButtonIndex != MPSearchScopeAll; + controller.searchBar.text = @""; if (controller.searchBar.showsScopeBar) controller.searchBar.scopeButtonTitles = @[@"All", @"Outdated"]; else @@ -315,13 +316,13 @@ MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = element.name; - cell.detailTextLabel.text = [NSString stringWithFormat:@"Used %d times, last on %@", - element.uses, [self.dateFormatter stringFromDate:element.lastUsed]]; + cell.detailTextLabel.text = PearlString(@"%d views, last on %@: %@", + element.uses, [self.dateFormatter stringFromDate:element.lastUsed], [element.algorithm shortNameOfType:element.type]); } else { // "New" section NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; cell.textLabel.text = query; - cell.detailTextLabel.text = @"Create a new site."; + cell.detailTextLabel.text = PearlString(@"Add new site: %@", [MPAlgorithmDefault shortNameOfType:[[MPAppDelegate get].activeUser defaultType]]); } } @@ -342,7 +343,7 @@ if (buttonIndex == [alert cancelButtonIndex]) return; - [MPAppDelegate managedObjectContextPerform:^(NSManagedObjectContext *moc) { + [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { MPUserEntity *activeUser = [[MPAppDelegate get] activeUserInContext:moc]; assert(activeUser); diff --git a/MasterPassword/iOS/MPTypeViewController.h b/MasterPassword/iOS/MPTypeViewController.h index d019087e..2bf4e122 100644 --- a/MasterPassword/iOS/MPTypeViewController.h +++ b/MasterPassword/iOS/MPTypeViewController.h @@ -8,12 +8,17 @@ #import +#import "MPEntities.h" + + @protocol MPTypeDelegate +@required - (void)didSelectType:(MPElementType)type; +- (MPElementType)selectedType; @optional -- (MPElementType)selectedType; +- (MPElementEntity *)selectedElement; @end diff --git a/MasterPassword/iOS/MPTypeViewController.m b/MasterPassword/iOS/MPTypeViewController.m index 6c681923..12d3e87e 100644 --- a/MasterPassword/iOS/MPTypeViewController.m +++ b/MasterPassword/iOS/MPTypeViewController.m @@ -7,6 +7,9 @@ // #import "MPTypeViewController.h" +#import "MPAppDelegate.h" +#import "MPAppDelegate_Store.h" +#import "MPAppDelegate_Key.h" @interface MPTypeViewController () @@ -64,8 +67,34 @@ UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; - if ([self.delegate respondsToSelector:@selector(selectedType)]) - cell.selected = ([self.delegate selectedType] == [self typeAtIndexPath:indexPath]); + MPElementEntity *selectedElement = nil; + if ([self.delegate respondsToSelector:@selector(selectedElement)]) + selectedElement = [self.delegate selectedElement]; + + MPElementType cellType = [self typeAtIndexPath:indexPath]; + MPElementType selectedType = selectedElement? selectedElement.type: [self.delegate selectedType]; + cell.selected = (selectedType == cellType); + + if (cellType != NSNotFound && cellType & MPElementTypeClassGenerated) { + [(UITextField *) [cell viewWithTag:2] setText:@"..."]; + + NSManagedObjectID *selectedElementOID = [selectedElement objectID]; + [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { + NSError *error; + MPElementGeneratedEntity *selectedElement_ = (MPElementGeneratedEntity *) [moc existingObjectWithID:selectedElementOID error:&error]; + if (!selectedElement_) { + err(@"Failed to retrieve element for password preview: %@", error); + return; + } + + selectedElement_.type = cellType; + NSString *typeContent = [selectedElement.algorithm generateContentForElement:selectedElement_ usingKey:[MPAppDelegate get].key]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [(UITextField *) [[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent]; + }); + }]; + } return cell; } @@ -98,9 +127,9 @@ case 3: return MPElementTypeGeneratedMedium; case 4: - return MPElementTypeGeneratedShort; - case 5: return MPElementTypeGeneratedBasic; + case 5: + return MPElementTypeGeneratedShort; case 6: return MPElementTypeGeneratedPIN; case 7: diff --git a/MasterPassword/iOS/MPUnlockViewController.m b/MasterPassword/iOS/MPUnlockViewController.m index edee334d..4feb757a 100644 --- a/MasterPassword/iOS/MPUnlockViewController.m +++ b/MasterPassword/iOS/MPUnlockViewController.m @@ -304,7 +304,7 @@ - (void)didSelectNewUserAvatar:(UIButton *)newUserAvatar { - [MPAppDelegate managedObjectContextPerform:^(NSManagedObjectContext *moc) { + [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) { MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPUserEntity class]) inManagedObjectContext:moc]; diff --git a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard index 147de1bf..5200f7a5 100644 --- a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard @@ -62,7 +62,7 @@ -