From fc60460935bcba446de05826c3e9c55e0f6646da Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Mon, 16 Jul 2012 20:29:48 +0200 Subject: [PATCH] Settings toggle and site user name. [ADDED] Allow saving a user name per site. This is an optional addition, toggled by tapping the new settings icon. Obviously user names can't be recovered after loss. --- External/Pearl | 2 +- MasterPassword-iOS.xcodeproj/project.pbxproj | 12 +- MasterPassword/MPElementEntity.h | 5 +- MasterPassword/MPElementEntity.m | 5 +- MasterPassword/MPTypes.h | 2 + .../MasterPassword 2.xcdatamodel/contents | 3 +- MasterPassword/iOS/MPMainViewController.h | 12 +- MasterPassword/iOS/MPMainViewController.m | 187 ++++- MasterPassword/iOS/MPSearchDelegate.m | 9 +- MasterPassword/iOS/MPiOSConfig.h | 2 + MasterPassword/iOS/MPiOSConfig.m | 4 +- .../iOS/MainStoryboard_iPhone.storyboard | 639 ++++++++++-------- 12 files changed, 551 insertions(+), 331 deletions(-) diff --git a/External/Pearl b/External/Pearl index 396fe5bd..032b71a8 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit 396fe5bd8a53adad376231ff32c56984f94c9c96 +Subproject commit 032b71a820d7f9526a42f51a00023e8247cd0943 diff --git a/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword-iOS.xcodeproj/project.pbxproj index 8c3e6ebb..ad704807 100644 --- a/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */; }; + DA0757EA15B3681200613FAA /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0757E915B3681200613FAA /* MPElementEntity.m */; }; DA0A1D0515690A9A0092735D /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0A1D0315690A9A0092735D /* Default.png */; }; DA0A1D0615690A9A0092735D /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0A1D0415690A9A0092735D /* Default@2x.png */; }; DA0A1D1515690AF40092735D /* Icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0A1D1315690AF30092735D /* Icon-72@2x.png */; }; @@ -675,7 +676,6 @@ DAB9FE1C15AC00C0007A7E5C /* MPElementGeneratedEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB9FE1B15AC00C0007A7E5C /* MPElementGeneratedEntity.m */; }; DAB9FE1F15AC00C0007A7E5C /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB9FE1E15AC00C0007A7E5C /* MPUserEntity.m */; }; DAB9FE2215AC00C0007A7E5C /* MPElementStoredEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB9FE2115AC00C0007A7E5C /* MPElementStoredEntity.m */; }; - DAB9FE2515AC00C0007A7E5C /* MPElementEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB9FE2415AC00C0007A7E5C /* MPElementEntity.m */; }; DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; }; DAC6325E1486805C0075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DAC6326D148680650075AEA5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; @@ -888,6 +888,8 @@ /* Begin PBXFileReference section */ DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; + DA0757E815B3681200613FAA /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = ""; }; + DA0757E915B3681200613FAA /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; sourceTree = ""; }; DA0A1D0315690A9A0092735D /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = Resources/Default.png; sourceTree = SOURCE_ROOT; }; DA0A1D0415690A9A0092735D /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default@2x.png"; path = "Resources/Default@2x.png"; sourceTree = SOURCE_ROOT; }; DA0A1D0715690AD40092735D /* tip_arrow_banana.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = tip_arrow_banana.png; path = Resources/Tooltips/tip_arrow_banana.png; sourceTree = SOURCE_ROOT; }; @@ -1646,8 +1648,6 @@ DAB9FE1E15AC00C0007A7E5C /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; }; DAB9FE2015AC00C0007A7E5C /* MPElementStoredEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementStoredEntity.h; sourceTree = ""; }; DAB9FE2115AC00C0007A7E5C /* MPElementStoredEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementStoredEntity.m; sourceTree = ""; }; - DAB9FE2315AC00C0007A7E5C /* MPElementEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementEntity.h; sourceTree = ""; }; - DAB9FE2415AC00C0007A7E5C /* MPElementEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementEntity.m; 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 = ""; }; @@ -1999,8 +1999,6 @@ DA5BFA50147E415C00F98B1E /* MasterPassword */ = { isa = PBXGroup; children = ( - DAB9FE2315AC00C0007A7E5C /* MPElementEntity.h */, - DAB9FE2415AC00C0007A7E5C /* MPElementEntity.m */, DAB9FE2015AC00C0007A7E5C /* MPElementStoredEntity.h */, DAB9FE2115AC00C0007A7E5C /* MPElementStoredEntity.m */, DAB9FE1D15AC00C0007A7E5C /* MPUserEntity.h */, @@ -2010,6 +2008,8 @@ DA0E07941577FE490008A67E /* MPEntities.h */, DA0E07951577FE490008A67E /* MPEntities.m */, DAB8D43C15036BCF00CED3BC /* MasterPassword.xcdatamodeld */, + DA0757E815B3681200613FAA /* MPElementEntity.h */, + DA0757E915B3681200613FAA /* MPElementEntity.m */, DA600C2415054F3A008E9AB6 /* MPAppDelegate_Key.h */, DA600C2315054F3A008E9AB6 /* MPAppDelegate_Key.m */, DA4426041557C1990052177D /* MPAppDelegate_Shared.h */, @@ -4248,7 +4248,7 @@ DAB9FE1C15AC00C0007A7E5C /* MPElementGeneratedEntity.m in Sources */, DAB9FE1F15AC00C0007A7E5C /* MPUserEntity.m in Sources */, DAB9FE2215AC00C0007A7E5C /* MPElementStoredEntity.m in Sources */, - DAB9FE2515AC00C0007A7E5C /* MPElementEntity.m in Sources */, + DA0757EA15B3681200613FAA /* MPElementEntity.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MasterPassword/MPElementEntity.h b/MasterPassword/MPElementEntity.h index ad0cce00..c02bf91d 100644 --- a/MasterPassword/MPElementEntity.h +++ b/MasterPassword/MPElementEntity.h @@ -2,7 +2,7 @@ // MPElementEntity.h // MasterPassword-iOS // -// Created by Maarten Billemont on 10/07/12. +// Created by Maarten Billemont on 15/07/12. // Copyright (c) 2012 Lyndir. All rights reserved. // @@ -16,10 +16,11 @@ @property (nonatomic, retain) id content; @property (nonatomic, retain) NSDate * lastUsed; @property (nonatomic, retain) NSString * name; +@property (nonatomic, retain) NSNumber * requiresExplicitMigration_; @property (nonatomic, retain) NSNumber * type_; @property (nonatomic, retain) NSNumber * uses_; @property (nonatomic, retain) NSNumber * version_; -@property (nonatomic, retain) NSNumber * requiresExplicitMigration_; +@property (nonatomic, retain) NSString * userName; @property (nonatomic, retain) MPUserEntity *user; @end diff --git a/MasterPassword/MPElementEntity.m b/MasterPassword/MPElementEntity.m index efbb68cb..9d11bf3c 100644 --- a/MasterPassword/MPElementEntity.m +++ b/MasterPassword/MPElementEntity.m @@ -2,7 +2,7 @@ // MPElementEntity.m // MasterPassword-iOS // -// Created by Maarten Billemont on 10/07/12. +// Created by Maarten Billemont on 15/07/12. // Copyright (c) 2012 Lyndir. All rights reserved. // @@ -15,10 +15,11 @@ @dynamic content; @dynamic lastUsed; @dynamic name; +@dynamic requiresExplicitMigration_; @dynamic type_; @dynamic uses_; @dynamic version_; -@dynamic requiresExplicitMigration_; +@dynamic userName; @dynamic user; @end diff --git a/MasterPassword/MPTypes.h b/MasterPassword/MPTypes.h index 37ee86e3..47794e17 100644 --- a/MasterPassword/MPTypes.h +++ b/MasterPassword/MPTypes.h @@ -45,9 +45,11 @@ typedef enum { #define MPCheckpointAction @"MPCheckpointAction" #define MPCheckpointHelpChapter @"MPCheckpointHelpChapter" #define MPCheckpointCopyToPasteboard @"MPCheckpointCopyToPasteboard" +#define MPCheckpointCopyUserNameToPasteboard @"MPCheckpointCopyUserNameToPasteboard" #define MPCheckpointResetPasswordCounter @"MPCheckpointResetPasswordCounter" #define MPCheckpointIncrementPasswordCounter @"MPCheckpointIncrementPasswordCounter" #define MPCheckpointEditPassword @"MPCheckpointEditPassword" +#define MPCheckpointEditUserName @"MPCheckpointEditUserName" #define MPCheckpointCloseAlert @"MPCheckpointCloseAlert" #define MPCheckpointUseType @"MPCheckpointUseType" #define MPCheckpointDeleteElement @"MPCheckpointDeleteElement" diff --git a/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents b/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents index a87e63c2..db179f25 100644 --- a/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents +++ b/MasterPassword/MasterPassword.xcdatamodeld/MasterPassword 2.xcdatamodel/contents @@ -6,6 +6,7 @@ + @@ -26,7 +27,7 @@ - + diff --git a/MasterPassword/iOS/MPMainViewController.h b/MasterPassword/iOS/MPMainViewController.h index 4ac1550d..1af97fbb 100644 --- a/MasterPassword/iOS/MPMainViewController.h +++ b/MasterPassword/iOS/MPMainViewController.h @@ -11,11 +11,11 @@ #import "MPElementEntity.h" #import "MPSearchDelegate.h" -@interface MPMainViewController : UIViewController +@interface MPMainViewController : UIViewController +@property (nonatomic, assign) BOOL showSettings; @property (strong, nonatomic) MPElementEntity *activeElement; @property (strong, nonatomic) IBOutlet MPSearchDelegate *searchDelegate; -@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *resetPasswordCounterGesture; @property (weak, nonatomic) IBOutlet UITextField *contentField; @property (weak, nonatomic) IBOutlet UIButton *typeButton; @property (weak, nonatomic) IBOutlet UIWebView *helpView; @@ -25,18 +25,23 @@ @property (weak, nonatomic) IBOutlet UIButton *passwordEdit; @property (weak, nonatomic) IBOutlet UIButton *passwordUpgrade; @property (weak, nonatomic) IBOutlet UIView *contentContainer; +@property (weak, nonatomic) IBOutlet UIView *displayContainer; @property (weak, nonatomic) IBOutlet UIView *helpContainer; @property (weak, nonatomic) IBOutlet UIView *contentTipContainer; +@property (weak, nonatomic) IBOutlet UIView *userNameTipContainer; @property (weak, nonatomic) IBOutlet UIView *alertContainer; @property (weak, nonatomic) IBOutlet UILabel *alertTitle; @property (weak, nonatomic) IBOutlet UITextView *alertBody; @property (weak, nonatomic) IBOutlet UILabel *contentTipBody; +@property (weak, nonatomic) IBOutlet UILabel *userNameTipBody; @property (weak, nonatomic) IBOutlet UIImageView *toolTipEditIcon; @property (weak, nonatomic) IBOutlet UIView *searchTipContainer; @property (weak, nonatomic) IBOutlet UIView *actionsTipContainer; @property (weak, nonatomic) IBOutlet UIView *typeTipContainer; @property (weak, nonatomic) IBOutlet UIView *toolTipContainer; @property (weak, nonatomic) IBOutlet UILabel *toolTipBody; +@property (weak, nonatomic) IBOutlet UIView *userNameContainer; +@property (weak, nonatomic) IBOutlet UITextField *userNameField; @property (copy) void (^contentTipCleanup)(BOOL finished); @property (copy) void (^toolTipCleanup)(BOOL finished); @@ -44,12 +49,13 @@ - (IBAction)copyContent; - (IBAction)incrementPasswordCounter; - (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender; +- (IBAction)editUserName:(UILongPressGestureRecognizer *)sender; - (IBAction)editPassword; - (IBAction)closeAlert; - (IBAction)upgradePassword; - (IBAction)action:(UIBarButtonItem *)sender; +- (IBAction)toggleSettings; -- (BOOL)isHelpVisible; - (void)toggleHelpAnimated:(BOOL)animated; - (void)setHelpHidden:(BOOL)hidden animated:(BOOL)animated; - (void)setHelpChapter:(NSString *)chapter; diff --git a/MasterPassword/iOS/MPMainViewController.m b/MasterPassword/iOS/MPMainViewController.m index 5716449d..4ea2c6f1 100644 --- a/MasterPassword/iOS/MPMainViewController.m +++ b/MasterPassword/iOS/MPMainViewController.m @@ -27,6 +27,7 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); @end @implementation MPMainViewController +@synthesize showSettings = _showSettings; @synthesize activeElement = _activeElement; @synthesize searchDelegate = _searchDelegate; @synthesize typeButton = _typeButton; @@ -37,19 +38,23 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); @synthesize passwordEdit = _passwordEdit; @synthesize passwordUpgrade = _passwordUpgrade; @synthesize contentContainer = _contentContainer; +@synthesize displayContainer = _displayContainer; @synthesize helpContainer = _helpContainer; @synthesize contentTipContainer = _copiedContainer; +@synthesize userNameTipContainer = _userNameTipContainer; @synthesize alertContainer = _alertContainer; @synthesize alertTitle = _alertTitle; @synthesize alertBody = _alertBody; @synthesize contentTipBody = _contentTipBody; +@synthesize userNameTipBody = _userNameTipBody; @synthesize toolTipEditIcon = _contentTipEditIcon; @synthesize searchTipContainer = _searchTipContainer; @synthesize actionsTipContainer = _actionsTipContainer; @synthesize typeTipContainer = _typeTipContainer; @synthesize toolTipContainer = _toolTipContainer; @synthesize toolTipBody = _toolTipBody; -@synthesize resetPasswordCounterGesture = _resetPasswordCounterGesture; +@synthesize userNameContainer = _userNameContainer; +@synthesize userNameField = _userNameField; @synthesize contentField = _contentField; @synthesize contentTipCleanup = _contentTipCleanup, toolTipCleanup = _toolTipCleanup; @@ -62,7 +67,8 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - [self setHelpHidden:![self isHelpVisible] animated:NO]; + [self updateHelpHiddenAnimated:NO]; + [self updateSettingsHiddenAnimated:NO]; } @@ -83,8 +89,9 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); self.searchDisplayController.searchResultsDelegate = self.searchDelegate; self.searchDisplayController.searchResultsDataSource = self.searchDelegate; - self.resetPasswordCounterGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(resetPasswordCounter:)]; - [self.passwordIncrementer addGestureRecognizer:self.resetPasswordCounterGesture]; + [self.passwordIncrementer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(resetPasswordCounter:)]]; + [self.userNameContainer addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(editUserName:)]]; + [self.userNameContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(copyUserName)]]; self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]]; @@ -127,7 +134,8 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); self.typeTipContainer.alpha = 0; self.toolTipContainer.alpha = 0; - [self setHelpHidden:[[MPiOSConfig get].helpHidden boolValue] animated:animated]; + [self updateHelpHiddenAnimated:NO]; + [self updateSettingsHiddenAnimated:NO]; [self updateAnimated:animated]; [super viewWillAppear:animated]; @@ -185,10 +193,13 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); [self setSearchTipContainer:nil]; [self setActionsTipContainer:nil]; [self setTypeTipContainer:nil]; - [self setSearchDelegate:nil]; - [self setResetPasswordCounterGesture:nil]; [self setToolTipContainer:nil]; [self setToolTipBody:nil]; + [self setDisplayContainer:nil]; + [self setUserNameField:nil]; + [self setUserNameTipContainer:nil]; + [self setUserNameTipBody:nil]; + [self setUserNameContainer:nil]; [super viewDidUnload]; } @@ -225,7 +236,8 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); forState:UIControlStateNormal]; self.typeButton.alpha = NSStringFromMPElementType(self.activeElement.type).length? 1: 0; - self.contentField.enabled = NO; + self.contentField.enabled = NO; + self.userNameField.enabled = NO; if ([self.activeElement isKindOfClass:[MPElementGeneratedEntity class]]) self.passwordCounter.text = PearlString(@"%u", ((MPElementGeneratedEntity *)self.activeElement).counter); @@ -241,31 +253,62 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); }); } -- (BOOL)isHelpVisible { - - return self.helpContainer.frame.origin.y == 216; -} - - (void)toggleHelpAnimated:(BOOL)animated { - [self setHelpHidden:[self isHelpVisible] animated:animated]; + [self setHelpHidden:![[MPiOSConfig get].helpHidden boolValue] animated:animated]; } - (void)setHelpHidden:(BOOL)hidden animated:(BOOL)animated { - dispatch_async(dispatch_get_main_queue(), ^{ - [UIView animateWithDuration:animated? 0.3f: 0 animations:^{ - if (hidden) { - self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, self.view.bounds.size.height - 44); - self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, self.view.bounds.size.height); - [MPiOSConfig get].helpHidden = [NSNumber numberWithBool:YES]; - } else { - self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 175); - self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 216); - [MPiOSConfig get].helpHidden = [NSNumber numberWithBool:NO]; - } + [MPiOSConfig get].helpHidden = PearlBool(hidden); + [self updateHelpHiddenAnimated:animated]; +} + +- (void)updateHelpHiddenAnimated:(BOOL)animated { + + if (animated) { + [UIView animateWithDuration:0.3f animations:^{ + [self updateHelpHiddenAnimated:NO]; }]; - }); + return; + } + + if ([[MPiOSConfig get].helpHidden boolValue]) { + self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, self.view.bounds.size.height - 44); + self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, self.view.bounds.size.height); + } else { + self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 225); + self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 266); + } +} + +- (IBAction)toggleSettings { + + [self toggleSettingsAnimated:YES]; +} + +- (void)toggleSettingsAnimated:(BOOL)animated { + + [MPiOSConfig get].settingsHidden = PearlBoolNot([MPiOSConfig get].settingsHidden); + self.showSettings = ![[MPiOSConfig get].settingsHidden boolValue]; + [self updateSettingsHiddenAnimated:animated]; +} + +- (void)updateSettingsHiddenAnimated:(BOOL)animated { + + if (animated) { + [UIView animateWithDuration:0.3f animations:^{ + [self updateSettingsHiddenAnimated:NO]; + }]; + return; + } + + if (self.showSettings) { + self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 137); + } else { + self.displayContainer.frame = CGRectSetHeight(self.displayContainer.frame, 87); + } + } - (void)setHelpChapter:(NSString *)chapter { @@ -315,6 +358,26 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); }); } +- (void)showUserNameTip:(NSString *)message { + + dispatch_async(dispatch_get_main_queue(), ^{ + self.userNameTipBody.text = message; + + [UIView animateWithDuration:0.3f animations:^{ + self.userNameTipContainer.alpha = 1; + } completion:^(BOOL finished) { + if (finished) { + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { + [UIView animateWithDuration:0.2f animations:^{ + self.userNameTipContainer.alpha = 0; + }]; + }); + } + }]; + }); +} + - (void)showToolTip:(NSString *)message withIcon:(UIImageView *)icon { dispatch_async(dispatch_get_main_queue(), ^{ @@ -376,6 +439,27 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCopyToPasteboard attributes:[NSDictionary dictionaryWithObjectsAndKeys: NSStringFromMPElementType(self.activeElement.type), @"type", + PearlUnsignedInteger(self.activeElement.version), + @"version", + nil]]; +} + +- (IBAction)copyUserName { + + if (!self.activeElement.userName) + return; + + inf(@"Copying user name for: %@", self.activeElement.name); + [UIPasteboard generalPasteboard].string = [self.activeElement.content description]; + + [self showUserNameTip:@"Copied!"]; + + [TestFlight passCheckpoint:MPCheckpointCopyUserNameToPasteboard]; + [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointCopyUserNameToPasteboard + attributes:[NSDictionary dictionaryWithObjectsAndKeys: + NSStringFromMPElementType(self.activeElement.type), @"type", + PearlUnsignedInteger(self.activeElement.version), + @"version", nil]]; } @@ -399,6 +483,8 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); attributes:[NSDictionary dictionaryWithObjectsAndKeys: NSStringFromMPElementType( self.activeElement.type), @"type", + PearlUnsignedInteger(self.activeElement.version), + @"version", nil]]; }]; } @@ -428,10 +514,34 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); attributes:[NSDictionary dictionaryWithObjectsAndKeys: NSStringFromMPElementType( self.activeElement.type), @"type", + PearlUnsignedInteger(self.activeElement.version), + @"version", nil]]; }]; } +- (IBAction)editUserName:(UILongPressGestureRecognizer *)sender { + + if (sender.state != UIGestureRecognizerStateBegan) + // Only fire when the gesture was first detected. + return; + + if (!self.activeElement) + return; + + self.userNameField.enabled = YES; + [self.userNameField becomeFirstResponder]; + + [TestFlight passCheckpoint:MPCheckpointEditUserName]; + [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointEditUserName attributes:[NSDictionary dictionaryWithObjectsAndKeys: + NSStringFromMPElementType( + self.activeElement.type), + @"type", + PearlUnsignedInteger(self.activeElement.version), + @"version", + nil]]; +} + - (void)changeElementWithWarning:(NSString *)warning do:(void (^)(void))task; { [PearlAlert showAlertWithTitle:@"Password Change" message:warning viewStyle:UIAlertViewStyleDefault @@ -474,6 +584,8 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); attributes:[NSDictionary dictionaryWithObjectsAndKeys: NSStringFromMPElementType( self.activeElement.type), @"type", + PearlUnsignedInteger(self.activeElement.version), + @"version", nil]]; } } @@ -616,7 +728,8 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); [TestFlight passCheckpoint:MPCheckpointAction]; } cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil otherTitles: - [self isHelpVisible]? @"Hide Help": @"Show Help", @"FAQ", @"Tutorial", @"Preferences", @"Feedback", @"Sign Out", nil]; + [[MPiOSConfig get].helpHidden boolValue]? @"Show Help": @"Hide Help", @"FAQ", @"Tutorial", @"Preferences", @"Feedback", @"Sign Out", + nil]; } - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result @@ -707,9 +820,12 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); NSStringFromMPElementType( self.activeElement.type), @"type", + PearlUnsignedInteger(self.activeElement.version), + @"version", nil]]; } + self.showSettings = ![[MPiOSConfig get].settingsHidden boolValue] || (element.userName != nil); [self updateAnimated:YES]; } @@ -717,6 +833,8 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); if (textField == self.contentField) [self.contentField resignFirstResponder]; + if (textField == self.userNameField) + [self.userNameField resignFirstResponder]; return YES; } @@ -737,6 +855,21 @@ void MPElementMigrate(MPElementEntity *entity, BOOL i); ((MPElementStoredEntity *)self.activeElement).content = self.contentField.text; }]; } + + if (textField == self.userNameField) { + self.userNameField.enabled = NO; + if (![[MPiOSConfig get].userNameTipShown boolValue]) { + [self showUserNameTip:@"Tap to copy or hold to edit."]; + [MPiOSConfig get].userNameTipShown = PearlBool(YES); + } + + if ([self.userNameField.text length]) + self.activeElement.userName = self.userNameField.text; + else + self.activeElement.userName = nil; + + [[MPAppDelegate get] saveContext]; + } } - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request diff --git a/MasterPassword/iOS/MPSearchDelegate.m b/MasterPassword/iOS/MPSearchDelegate.m index 419db494..7060683d 100644 --- a/MasterPassword/iOS/MPSearchDelegate.m +++ b/MasterPassword/iOS/MPSearchDelegate.m @@ -39,7 +39,7 @@ fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]]; self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[MPAppDelegate managedObjectContext] - sectionNameKeyPath:nil cacheName:nil]; + sectionNameKeyPath:nil cacheName:nil]; self.fetchedResultsController.delegate = self; self.tipView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 170)]; @@ -129,7 +129,8 @@ assert(self.query); self.fetchedResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name BEGINSWITH[cd] %@) AND user == %@", - self.query, self.query, NilToNSNull([MPAppDelegate get].activeUser)]; + self.query, self.query, + NilToNSNull([MPAppDelegate get].activeUser)]; NSError *error; if (![self.fetchedResultsController performFetch:&error]) @@ -316,7 +317,7 @@ [self.delegate didSelectElement:element]; }); }]; - } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; + } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; } } @@ -353,6 +354,8 @@ forRowAtIndexPath:(NSIndexPath *)indexPath { [[LocalyticsSession sharedLocalyticsSession] tagEvent:MPCheckpointDeleteElement attributes:[NSDictionary dictionaryWithObjectsAndKeys: NSStringFromMPElementType(element.type), @"type", + PearlUnsignedInteger(element.version), + @"version", nil]]; }]; } diff --git a/MasterPassword/iOS/MPiOSConfig.h b/MasterPassword/iOS/MPiOSConfig.h index 4bd4acab..ea69e8b7 100644 --- a/MasterPassword/iOS/MPiOSConfig.h +++ b/MasterPassword/iOS/MPiOSConfig.h @@ -12,9 +12,11 @@ @property (nonatomic, retain) NSNumber *sendInfo; @property (nonatomic, retain) NSNumber *helpHidden; +@property (nonatomic, retain) NSNumber *settingsHidden; @property (nonatomic, retain) NSNumber *showQuickStart; @property (nonatomic, retain) NSNumber *actionsTipShown; @property (nonatomic, retain) NSNumber *typeTipShown; +@property (nonatomic, retain) NSNumber *userNameTipShown; + (MPiOSConfig *)get; diff --git a/MasterPassword/iOS/MPiOSConfig.m b/MasterPassword/iOS/MPiOSConfig.m index 2f1231c2..1e056502 100644 --- a/MasterPassword/iOS/MPiOSConfig.m +++ b/MasterPassword/iOS/MPiOSConfig.m @@ -7,7 +7,7 @@ // @implementation MPiOSConfig -@dynamic sendInfo, helpHidden, showQuickStart, actionsTipShown, typeTipShown; +@dynamic sendInfo, helpHidden, settingsHidden, showQuickStart, actionsTipShown, typeTipShown, userNameTipShown; - (id)init { @@ -17,10 +17,12 @@ [self.defaults registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(sendInfo)), [NSNumber numberWithBool:NO], NSStringFromSelector(@selector(helpHidden)), + [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(settingsHidden)), [NSNumber numberWithBool:YES], NSStringFromSelector(@selector(showQuickStart)), @"510296984", NSStringFromSelector(@selector(iTunesID)), PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(actionsTipShown)), PearlBoolNot(self.firstRun), NSStringFromSelector(@selector(typeTipShown)), + PearlBool(NO), NSStringFromSelector(@selector(userNameTipShown)), nil]]; return self; diff --git a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard index 407bcb8d..baf68ba2 100644 --- a/MasterPassword/iOS/MainStoryboard_iPhone.storyboard +++ b/MasterPassword/iOS/MainStoryboard_iPhone.storyboard @@ -1,7 +1,8 @@ - + - + + @@ -27,23 +28,23 @@ - @@ -62,21 +63,21 @@ -