2
0

Clean up old UI.

[ADDED]     Password strength to type cells.
[FIXED]     Lots of misc UI fixes.
[ADDED]     Footer links in pull-down.
This commit is contained in:
Maarten Billemont 2014-06-03 21:04:22 -04:00
parent c8eb2fdde7
commit c1c15ddb89
35 changed files with 326 additions and 7006 deletions

View File

@ -1,41 +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
*/
//
// MPAppViewController
//
// Created by Maarten Billemont on 2012-08-31.
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
//
#import "MPAppViewController.h"
@implementation MPAppViewController {
}
- (IBAction)gorillas:(UIButton *)sender {
MPCheckpoint( MPCheckpointApp, @{
@"app" : @"gorillas"
} );
[UIApp openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/lyndir/gorillas/id302275459?mt=8"]];
}
- (IBAction)deblock:(UIButton *)sender {
MPCheckpoint( MPCheckpointApp, @{
@"app" : @"deblock"
} );
[UIApp openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/lyndir/deblock/id325058485?mt=8"]];
}
@end

View File

@ -1,26 +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
*/
//
// MPAppsViewController
//
// Created by Maarten Billemont on 2012-08-31.
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MPAppsViewController : UIViewController<UIPageViewControllerDataSource, UIPageViewControllerDelegate>
@property(weak, nonatomic) IBOutlet UIImageView *pagePositionView;
- (IBAction)exit;
@end

View File

@ -1,117 +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
*/
//
// MPAppsViewController
//
// Created by Maarten Billemont on 2012-08-31.
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
//
#import "MPAppsViewController.h"
@interface MPAppsViewController()
@property(nonatomic, strong) NSMutableArray *pageVCs;
@property(nonatomic, strong) UIPageViewController *pageViewController;
@end
@implementation MPAppsViewController {
}
@synthesize pagePositionView = _pagePositionView;
@synthesize pageVCs = _pageVCs;
@synthesize pageViewController = _pageViewController;
- (void)viewDidLoad {
self.pageVCs = [NSMutableArray array];
UIViewController *vc;
@try {
for (NSUInteger p = 0;
(vc = [self.storyboard instantiateViewControllerWithIdentifier:PearlString( @"MPAppViewController_%lu", (long)p )]);
++p)
[self.pageVCs addObject:vc];
}
@catch (NSException *e) {
if (![e.name isEqualToString:NSInvalidArgumentException])
[e raise];
}
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:nil];
self.pageViewController.dataSource = self;
self.pageViewController.delegate = self;
self.pageViewController.view.frame = CGRectFromOriginWithSize( CGPointZero, self.pagePositionView.bounds.size );
[self addChildViewController:self.pageViewController];
[self.pagePositionView addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
[UIApp setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
MPCheckpoint( MPCheckpointApps, nil );
[self.pageViewController setViewControllers:@[ (self.pageVCs)[1] ] direction:UIPageViewControllerNavigationDirectionForward
animated:NO completion:nil];
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[self.pageViewController setViewControllers:@[ (self.pageVCs)[0] ] direction:UIPageViewControllerNavigationDirectionForward
animated:YES completion:nil];
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[UIApp setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[super viewWillDisappear:animated];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate {
return NO;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController {
NSUInteger vcIndex = [self.pageVCs indexOfObject:viewController];
return (self.pageVCs)[(vcIndex + [self.pageVCs count] - 1) % self.pageVCs.count];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController {
NSUInteger vcIndex = [self.pageVCs indexOfObject:viewController];
return (self.pageVCs)[(vcIndex + 1) % self.pageVCs.count];
}
- (IBAction)exit {
[self dismissViewControllerAnimated:YES completion:nil];
}
@end

View File

@ -206,13 +206,11 @@ const long MPAvatarAdd = 10000;
}
switch (self.mode) {
case MPAvatarModeLowered: {
self.avatarSizeConstraint.constant = self.avatarImageView.image.size.height;
self.avatarRaisedConstraint.priority = UILayoutPriorityDefaultLow;
self.avatarToTopConstraint.priority = UILayoutPriorityDefaultLow;
self.nameToCenterConstraint.priority = UILayoutPriorityDefaultLow;
[self.avatarSizeConstraint layoutWithConstant:self.avatarImageView.image.size.height];
[self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultLow];
[self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultLow];
self.nameContainer.alpha = self.visibility;
self.nameContainer.backgroundColor = [UIColor clearColor];
self.avatarImageView.alpha = self.visibility / 0.7f + 0.3f;
@ -220,10 +218,10 @@ const long MPAvatarAdd = 10000;
break;
}
case MPAvatarModeRaisedButInactive: {
self.avatarSizeConstraint.constant = self.avatarImageView.image.size.height;
self.avatarRaisedConstraint.priority = UILayoutPriorityDefaultHigh;
self.avatarToTopConstraint.priority = UILayoutPriorityDefaultLow;
self.nameToCenterConstraint.priority = UILayoutPriorityDefaultLow;
[self.avatarSizeConstraint layoutWithConstant:self.avatarImageView.image.size.height];
[self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultLow];
self.nameContainer.alpha = self.visibility;
self.nameContainer.backgroundColor = [UIColor clearColor];
self.avatarImageView.alpha = 0;
@ -231,10 +229,10 @@ const long MPAvatarAdd = 10000;
break;
}
case MPAvatarModeRaisedAndActive: {
self.avatarSizeConstraint.constant = self.avatarImageView.image.size.height;
self.avatarRaisedConstraint.priority = UILayoutPriorityDefaultHigh;
self.avatarToTopConstraint.priority = UILayoutPriorityDefaultLow;
self.nameToCenterConstraint.priority = UILayoutPriorityDefaultHigh;
[self.avatarSizeConstraint layoutWithConstant:self.avatarImageView.image.size.height];
[self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
self.nameContainer.alpha = self.visibility;
self.nameContainer.backgroundColor = [UIColor blackColor];
self.avatarImageView.alpha = 1;
@ -242,10 +240,10 @@ const long MPAvatarAdd = 10000;
break;
}
case MPAvatarModeRaisedAndHidden: {
self.avatarSizeConstraint.constant = self.avatarImageView.image.size.height;
self.avatarRaisedConstraint.priority = UILayoutPriorityDefaultHigh;
self.avatarToTopConstraint.priority = UILayoutPriorityDefaultLow;
self.nameToCenterConstraint.priority = UILayoutPriorityDefaultHigh;
[self.avatarSizeConstraint layoutWithConstant:self.avatarImageView.image.size.height];
[self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
[self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultLow];
[self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
self.nameContainer.alpha = 0;
self.nameContainer.backgroundColor = [UIColor blackColor];
self.avatarImageView.alpha = 0;
@ -253,20 +251,16 @@ const long MPAvatarAdd = 10000;
break;
}
case MPAvatarModeRaisedAndMinimized: {
self.avatarSizeConstraint.constant = 36;
self.avatarRaisedConstraint.priority = UILayoutPriorityDefaultLow;
self.avatarToTopConstraint.priority = UILayoutPriorityDefaultHigh;
self.nameToCenterConstraint.priority = UILayoutPriorityDefaultHigh;
[self.avatarSizeConstraint layoutWithConstant:36];
[self.avatarRaisedConstraint layoutWithPriority:UILayoutPriorityDefaultLow];
[self.avatarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
[self.nameToCenterConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
self.nameContainer.alpha = 0;
self.nameContainer.backgroundColor = [UIColor blackColor];
self.avatarImageView.alpha = 1;
break;
}
}
[self.avatarSizeConstraint apply];
[self.avatarRaisedConstraint apply];
[self.avatarToTopConstraint apply];
[self.nameToCenterConstraint apply];
// Avatar minimized.
if (self.mode == MPAvatarModeRaisedAndMinimized)

View File

@ -1,27 +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
*/
//
// MPElementListAllViewController
//
// Created by Maarten Billemont on 2013-01-31.
// Copyright 2013 lhunath (Maarten Billemont). All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPElementListController.h"
@interface MPElementListAllViewController : MPElementListController
- (IBAction)close:(id)sender;
- (IBAction)add:(id)sender;
@end

View File

@ -1,188 +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
*/
//
// MPElementListAllViewController
//
// Created by Maarten Billemont on 2013-01-31.
// Copyright 2013 lhunath (Maarten Billemont). All rights reserved.
//
#import "MPElementListAllViewController.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
#define MPElementUpgradeOldContentKey @"MPElementUpgradeOldContentKey"
#define MPElementUpgradeNewContentKey @"MPElementUpgradeNewContentKey"
@implementation MPElementListAllViewController
- (BOOL)prefersStatusBarHidden {
return NO;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if ([self.filter isEqualToString:MPElementListFilterNone]) {
self.navigationItem.title = @"All Sites";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add:)];
}
else if ([self.filter isEqualToString:MPElementListFilterOutdated]) {
self.navigationItem.title = @"Outdated";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithTitle:@"Upgrade All" style:UIBarButtonItemStyleBordered target:self action:@selector(upgradeAll:)];
}
[self updateData];
}
- (IBAction)close:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)add:(id)sender {
[PearlAlert showAlertWithTitle:@"Add Site" message:nil viewStyle:UIAlertViewStylePlainTextInput initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (alert.cancelButtonIndex == buttonIndex)
return;
__weak MPElementListAllViewController *wSelf = self;
[[MPiOSAppDelegate get] addElementNamed:[alert textFieldAtIndex:0].text completion:^(MPElementEntity *element) {
if (element)
PearlMainQueue( ^{
[wSelf.delegate didSelectElement:element];
[wSelf close:nil];
} );
}];
}
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonOkay, nil];
}
- (IBAction)upgradeAll:(id)sender {
[PearlAlert showAlertWithTitle:@"Upgrading All Sites"
message:@"You are about to upgrade all outdated sites. This will cause passwords to change. "
@"Afterwards, you can get an overview of the changes."
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:
^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
PearlOverlay *activity = [PearlOverlay showProgressOverlayWithTitle:@"Upgrading Sites"];
[self performUpgradeAllWithCompletion:^(BOOL success, NSDictionary *changes) {
dispatch_async( dispatch_get_main_queue(), ^{
[self showUpgradeChanges:changes];
[activity cancelOverlayAnimated:YES];
} );
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil];
}
- (void)performUpgradeAllWithCompletion:(void (^)(BOOL success, NSDictionary *changes))completion {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.fetchBatchSize = 20;
NSError *error = nil;
NSArray *elements = [moc executeFetchRequest:fetchRequest error:&error];
if (!elements) {
err(@"Failed to fetch outdated sites for upgrade: %@", error);
completion( NO, nil );
return;
}
MPKey *key = [MPAppDelegate_Shared get].key;
NSMutableDictionary *elementChanges = [NSMutableDictionary dictionaryWithCapacity:[elements count]];
for (MPElementEntity *element in elements) {
id oldContent = [element.algorithm resolveContentForElement:element usingKey:key];
[element migrateExplicitly:YES];
id newContent = [element.algorithm resolveContentForElement:element usingKey:key];
if (!(element.type & MPElementFeatureDevicePrivate) && (!oldContent || ![oldContent isEqual:newContent]))
elementChanges[element.name] = @{
MPElementUpgradeOldContentKey : oldContent,
MPElementUpgradeNewContentKey : newContent,
};
}
[moc saveToStore];
completion( YES, elementChanges );
}];
}
- (void)showUpgradeChanges:(NSDictionary *)changes {
if (![changes count])
return;
NSMutableString *formattedChanges = [NSMutableString new];
for (NSString *changedElementName in changes) {
NSDictionary *elementChanges = changes[changedElementName];
id oldContent = elementChanges[MPElementUpgradeOldContentKey];
id newContent = elementChanges[MPElementUpgradeNewContentKey];
[formattedChanges appendFormat:@"%@: %@ -> %@\n", changedElementName, oldContent, newContent];
}
[PearlAlert showAlertWithTitle:@"Sites Upgraded"
message:PearlString( @"This upgrade has caused %lu passwords to change.\n"
@"To make updating the actual passwords of these accounts easier, "
@"you can email a summary of these changes to yourself.", (unsigned long)[changes count] )
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[PearlEMail sendEMailTo:nil fromVC:self subject:@"[Master Password] Upgrade Changes" body:formattedChanges];
} cancelTitle:@"Don't Email" otherTitles:@"Send Email", nil];
}
- (NSFetchedResultsController *)fetchedResultsControllerByUses {
return nil;
}
- (void)configureFetchRequest:(NSFetchRequest *)fetchRequest {
fetchRequest.fetchBatchSize = 10;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return nil;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return nil;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
[self close:nil];
}
@end

View File

@ -1,253 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1552</int>
<string key="IBDocument.SystemVersion">12D78</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1187.37</string>
<string key="IBDocument.HIToolboxVersion">626.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">2083</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUIImageView</string>
<string>IBUILabel</string>
<string>IBUITableViewCell</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="975951072">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUITableViewCell" id="1072502652">
<reference key="NSNextResponder"/>
<int key="NSvFlags">1280</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUIView" id="70825627">
<reference key="NSNextResponder" ref="1072502652"/>
<int key="NSvFlags">1280</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUILabel" id="169671678">
<reference key="NSNextResponder" ref="70825627"/>
<int key="NSvFlags">1280</int>
<object class="NSPSMatrix" key="NSFrameMatrix"/>
<string key="NSFrame">{{10, 4}, {38, 22}}</string>
<reference key="NSSuperview" ref="70825627"/>
<reference key="NSNextKeyView" ref="35578451"/>
<object class="NSColor" key="IBUIBackgroundColor" id="801193159">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MCAwAA</bytes>
</object>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">7</int>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">Title</string>
<object class="NSColor" key="IBUITextColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<object class="NSColor" key="IBUIHighlightedColor" id="748798155">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEAA</bytes>
</object>
<int key="IBUIBaselineAdjustment">0</int>
<object class="IBUIFontDescription" key="IBUIFontDescription">
<int key="type">2</int>
<double key="pointSize">18</double>
</object>
<object class="NSFont" key="IBUIFont">
<string key="NSName">Helvetica-Bold</string>
<double key="NSSize">18</double>
<int key="NSfFlags">16</int>
</object>
<bool key="IBUIAdjustsFontSizeToFit">NO</bool>
</object>
<object class="IBUILabel" id="35578451">
<reference key="NSNextResponder" ref="70825627"/>
<int key="NSvFlags">1280</int>
<object class="NSPSMatrix" key="NSFrameMatrix"/>
<string key="NSFrame">{{10, 26}, {47, 18}}</string>
<reference key="NSSuperview" ref="70825627"/>
<reference key="IBUIBackgroundColor" ref="801193159"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">7</int>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<string key="IBUIText">Subtitle</string>
<object class="NSColor" key="IBUITextColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
</object>
<reference key="IBUIHighlightedColor" ref="748798155"/>
<int key="IBUIBaselineAdjustment">0</int>
<object class="IBUIFontDescription" key="IBUIFontDescription">
<int key="type">1</int>
<double key="pointSize">14</double>
</object>
<object class="NSFont" key="IBUIFont">
<string key="NSName">Helvetica</string>
<double key="NSSize">14</double>
<int key="NSfFlags">16</int>
</object>
<bool key="IBUIAdjustsFontSizeToFit">NO</bool>
</object>
</array>
<object class="NSPSMatrix" key="NSFrameMatrix"/>
<string key="NSFrameSize">{320, 47}</string>
<reference key="NSSuperview" ref="1072502652"/>
<reference key="NSNextKeyView" ref="169671678"/>
<reference key="IBUIBackgroundColor" ref="801193159"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
<int key="IBUIContentMode">4</int>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</array>
<object class="NSPSMatrix" key="NSFrameMatrix"/>
<string key="NSFrameSize">{320, 48}</string>
<reference key="NSSuperview"/>
<reference key="NSNextKeyView" ref="70825627"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MAA</bytes>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIHidesAccessoryWhenEditing">NO</bool>
<int key="IBUIIndentationLevel">1</int>
<float key="IBUIIndentationWidth">0.0</float>
<reference key="IBUIContentView" ref="70825627"/>
<string key="IBUIReuseIdentifier">MPElementListCell</string>
<integer value="3" key="IBUIStyle"/>
<reference key="IBUITextLabel" ref="169671678"/>
<reference key="IBUIDetailTextLabel" ref="35578451"/>
</object>
<object class="IBUIImageView" id="410525493">
<nil key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<string key="NSFrameSize">{320, 48}</string>
<string key="NSReuseIdentifierKey">_NS:9</string>
<reference key="IBUIBackgroundColor" ref="801193159"/>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="IBUIContentStretch">{{0.10000000000000001, 0.10000000000000001}, {0.79999999999999982, 0.79999999999999982}}</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSCustomResource" key="IBUIImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">ui_list_middle.png</string>
</object>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="1072502652"/>
</object>
<int key="connectionID">11</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">backgroundView</string>
<reference key="source" ref="1072502652"/>
<reference key="destination" ref="410525493"/>
</object>
<int key="connectionID">10</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="975951072"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">4</int>
<reference key="object" ref="1072502652"/>
<array class="NSMutableArray" key="children">
<reference ref="35578451"/>
<reference ref="169671678"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">5</int>
<reference key="object" ref="35578451"/>
<reference key="parent" ref="1072502652"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">6</int>
<reference key="object" ref="169671678"/>
<reference key="parent" ref="1072502652"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">8</int>
<reference key="object" ref="410525493"/>
<reference key="parent" ref="0"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">UIViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="4.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<reference key="4.IBUserGuides" ref="0"/>
<boolean value="NO" key="4.showNotes"/>
<string key="5.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<reference key="5.IBUserGuides" ref="0"/>
<boolean value="NO" key="5.showNotes"/>
<string key="6.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<reference key="6.IBUserGuides" ref="0"/>
<boolean value="NO" key="6.showNotes"/>
<string key="8.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">11</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<string key="NS.key.0">ui_list_middle.png</string>
<string key="NS.object.0">{300, 34}</string>
</object>
<string key="IBCocoaTouchPluginVersion">2083</string>
</data>
</archive>

View File

@ -1,21 +0,0 @@
#import <Foundation/Foundation.h>
#import "MPElementListDelegate.h"
#define MPElementListFilterNone @"MPElementListFilterNone"
#define MPElementListFilterOutdated @"MPElementListFilterOutdated"
@interface MPElementListController : UITableViewController<NSFetchedResultsControllerDelegate>
@property(weak, nonatomic) IBOutlet id<MPElementListDelegate> delegate;
@property(strong, nonatomic) NSString *filter;
@property(readonly) NSFetchedResultsController *fetchedResultsControllerByUses;
@property(readonly) NSFetchedResultsController *fetchedResultsControllerByLastUsed;
@property(readonly) NSDateFormatter *dateFormatter;
- (void)updateData;
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atTableIndexPath:(NSIndexPath *)indexPath;
- (void)customTableViewUpdates;
@end

View File

@ -1,279 +0,0 @@
#import "MPElementListController.h"
#import "MPAppDelegate_Store.h"
#import "MPiOSAppDelegate.h"
@interface MPElementListController()
@end
@implementation MPElementListController {
NSFetchedResultsController *_fetchedResultsControllerByUses;
NSFetchedResultsController *_fetchedResultsControllerByLastUsed;
NSDateFormatter *_dateFormatter;
}
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
[self updateData];
}];
[super viewDidLoad];
}
- (NSFetchedResultsController *)fetchedResultsControllerByLastUsed {
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must be accessed from the main thread.");
if (!_fetchedResultsControllerByLastUsed) {
NSManagedObjectContext *context = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
if (!context)
return nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(lastUsed) ) ascending:NO] ];
[self configureFetchRequest:fetchRequest];
_fetchedResultsControllerByLastUsed = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsControllerByLastUsed.delegate = self;
}
return _fetchedResultsControllerByLastUsed;
}
- (NSFetchedResultsController *)fetchedResultsControllerByUses {
NSAssert([[NSThread currentThread] isMainThread], @"The fetchedResultsController must be accessed from the main thread.");
if (!_fetchedResultsControllerByUses) {
NSManagedObjectContext *context = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
if (!context)
return nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector(uses_) ) ascending:NO] ];
[self configureFetchRequest:fetchRequest];
_fetchedResultsControllerByUses = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsControllerByUses.delegate = self;
}
return _fetchedResultsControllerByUses;
}
- (void)configureFetchRequest:(NSFetchRequest *)fetchRequest {
fetchRequest.fetchLimit = 5;
}
- (NSDateFormatter *)dateFormatter {
if (!_dateFormatter)
(_dateFormatter = [NSDateFormatter new]).dateStyle = NSDateFormatterShortStyle;
return _dateFormatter;
}
- (void)updateData {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:self.fetchedResultsControllerByLastUsed.managedObjectContext];
if (!activeUser) {
_fetchedResultsControllerByLastUsed = nil;
_fetchedResultsControllerByUses = nil;
[self.tableView reloadData];
return;
}
// Build predicate.
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@", activeUser];
// Add query predicate.
UISearchBar *searchBar = self.searchDisplayController.searchBar;
if (searchBar) {
NSString *query = [searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (!query)
return;
predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
@[ predicate, [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", query] ]];
}
// Add filter predicate.
if ([self.filter isEqualToString:MPElementListFilterOutdated])
predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
@[ [NSPredicate predicateWithFormat:@"requiresExplicitMigration_ == YES"], predicate ]];
// Fetch
NSError *error;
self.fetchedResultsControllerByLastUsed.fetchRequest.predicate = predicate;
self.fetchedResultsControllerByUses.fetchRequest.predicate = predicate;
if (self.fetchedResultsControllerByLastUsed && ![self.fetchedResultsControllerByLastUsed performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
if (self.fetchedResultsControllerByUses && ![self.fetchedResultsControllerByUses performFetch:&error])
err(@"Couldn't fetch elements: %@", error);
[self.tableView reloadData];
}
- (void)customTableViewUpdates {
}
// See MP-14, also crashes easily on internal assertions etc..
//- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
//
// [self.tableView beginUpdates];
//}
//
//- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
// atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
//
// switch (type) {
//
// case NSFetchedResultsChangeInsert:
// [self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// break;
//
// case NSFetchedResultsChangeDelete:
// [self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// break;
//
// case NSFetchedResultsChangeUpdate:
// [self.tableView reloadRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// break;
//
// case NSFetchedResultsChangeMove:
// [self.tableView deleteRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:indexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// [self.tableView insertRowsAtIndexPaths:@[ [self tableIndexPathForFetchController:controller indexPath:newIndexPath] ]
// withRowAnimation:UITableViewRowAnimationAutomatic];
// break;
// }
//}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// [self customTableViewUpdates];
// [self.tableView endUpdates];
[self.tableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0)
return (NSInteger)[[[self.fetchedResultsControllerByLastUsed sections] lastObject] numberOfObjects];
if (section == 1)
return (NSInteger)[[[self.fetchedResultsControllerByUses sections] lastObject] numberOfObjects];
Throw(@"Unsupported section: %ld", (long)section);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MPElementListCell"];
if (!cell)
cell = (UITableViewCell *)[[UIViewController alloc] initWithNibName:@"MPElementListCellView" bundle:nil].view;
[self configureCell:cell inTableView:tableView atTableIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atTableIndexPath:(NSIndexPath *)indexPath {
MPElementEntity *element = [self elementForTableIndexPath:indexPath];
cell.textLabel.text = element.name;
cell.detailTextLabel.text = PearlString( @"%lu views, last on %@: %@",
(unsigned long)element.uses, [self.dateFormatter stringFromDate:element.lastUsed],
[element.algorithm shortNameOfType:element.type] );
}
- (NSIndexPath *)tableIndexPathForFetchController:(NSFetchedResultsController *)fetchedResultsController
indexPath:(NSIndexPath *)indexPath {
if (fetchedResultsController == self.fetchedResultsControllerByLastUsed)
return [NSIndexPath indexPathForRow:indexPath.row inSection:0];
if (fetchedResultsController == self.fetchedResultsControllerByUses)
return [NSIndexPath indexPathForRow:indexPath.row inSection:1];
Throw(@"Unknown fetched results controller: %@, for index path: %@", fetchedResultsController, indexPath);
}
- (NSIndexPath *)fetchedIndexPathForTableIndexPath:(NSIndexPath *)indexPath {
return [NSIndexPath indexPathForRow:indexPath.row inSection:0];
}
- (MPElementEntity *)elementForTableIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0)
return [self.fetchedResultsControllerByLastUsed objectAtIndexPath:[self fetchedIndexPathForTableIndexPath:indexPath]];
if (indexPath.section == 1)
return [self.fetchedResultsControllerByUses objectAtIndexPath:[self fetchedIndexPathForTableIndexPath:indexPath]];
Throw(@"Unsupported section: %ld", (long)indexPath.section);
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.delegate didSelectElement:[self elementForTableIndexPath:indexPath]];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section == 0)
return @"Most Recently Used";
if (section == 1)
return @"Most Commonly Used";
Throw(@"Unsupported section: %ld", (long)section);
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return @[ @"recency", @"uses" ];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return index;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectID *elementOID = [self elementForTableIndexPath:indexPath].objectID;
[MPiOSAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
MPElementEntity *element = [MPElementEntity existingObjectWithID:elementOID inContext:context];
if (!element)
return;
inf(@"Deleting element: %@", element.name);
[context deleteObject:element];
[context saveToStore];
MPCheckpoint( MPCheckpointDeleteElement, @{
@"type" : NilToNSNull(element.typeName),
@"version" : @(element.version)
} );
}];
}
}
@end

View File

@ -1,24 +0,0 @@
/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPElementListDelegate
//
// Created by Maarten Billemont on 2013-01-31.
// Copyright 2013 lhunath (Maarten Billemont). All rights reserved.
//
#import "MPElementEntity.h"
@protocol MPElementListDelegate<NSObject>
- (void)didSelectElement:(MPElementEntity *)element;
@end

View File

@ -1,19 +0,0 @@
//
// MPSearchDelegate.h
// MasterPassword
//
// Created by Maarten Billemont on 04/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MPElementListController.h"
@interface MPElementListSearchController : MPElementListController<UISearchBarDelegate, UISearchDisplayDelegate>
@property(strong, nonatomic) UILabel *tipView;
@property(strong, nonatomic) IBOutlet UISearchDisplayController *searchDisplayController;
@property(weak, nonatomic) IBOutlet UIView *searchTipContainer;
@end

View File

@ -1,247 +0,0 @@
//
// MPSearchDelegate.m
// MasterPassword
//
// Created by Maarten Billemont on 04/01/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "MPElementListSearchController.h"
#import "MPMainViewController.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
@interface MPElementListSearchController()
@property(nonatomic) BOOL newSiteSectionWasNeeded;
@end
@implementation MPElementListSearchController
@synthesize searchDisplayController;
- (id)init {
if (!(self = [super init]))
return nil;
self.tipView = [[UILabel alloc] initWithFrame:CGRectMake( 0, 0, 320, 170 )];
self.tipView.textAlignment = NSTextAlignmentCenter;
self.tipView.backgroundColor = [UIColor clearColor];
self.tipView.textColor = [UIColor lightTextColor];
self.tipView.shadowColor = [UIColor blackColor];
self.tipView.shadowOffset = CGSizeMake( 0, -1 );
self.tipView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
| UIViewAutoresizingFlexibleBottomMargin;
self.tipView.numberOfLines = 0;
self.tipView.font = [UIFont systemFontOfSize:14];
self.tipView.text =
@"Tip:\n"
@"Name your sites by their domain name:\n"
@"apple.com, twitter.com\n\n"
@"For email accounts, use the address:\n"
@"john@apple.com, john@gmail.com";
return self;
}
- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar {
[((MPMainViewController *)self.delegate) performSegueWithIdentifier:@"MP_AllSites" sender:MPElementListFilterNone];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// Simulate a tap on the first visible row.
UITableView *tableView = self.searchDisplayController.searchResultsTableView;
for (NSInteger section = 0; section < [self numberOfSectionsInTableView:tableView]; ++section) {
if (![self tableView:tableView numberOfRowsInSection:section])
continue;
[self tableView:tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
}
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
controller.searchBar.text = @"";
[UIView animateWithDuration:0.2f animations:^{
self.searchTipContainer.alpha = 0;
}];
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
[self updateData];
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
controller.searchBar.prompt = nil;
controller.searchBar.searchResultsButtonSelected = NO;
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
tableView.backgroundColor = [UIColor blackColor];
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.rowHeight = 48.0f;
self.tableView = tableView;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self updateData];
return NO;
}
- (void)updateData {
[super updateData];
UISearchBar *searchBar = self.searchDisplayController.searchBar;
CGRect searchBarFrame = searchBar.frame;
[searchBar.superview enumerateViews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
if ([subview isKindOfClass:[UIControl class]] &&
CGPointEqualToPoint(
CGPointDistanceBetweenCGPoints( searchBarFrame.origin, subview.frame.origin ),
CGPointMake( 0, searchBarFrame.size.height ) )) {
dispatch_async( dispatch_get_main_queue(), ^{
[self.tipView removeFromSuperview];
[subview addSubview:self.tipView];
} );
*stop = YES;
}
} recurse:NO];
}
- (BOOL)newSiteSectionNeeded {
NSString *query = [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (![query length])
return NO;
__block BOOL hasExactQueryMatch = NO;
id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsControllerByUses sections] lastObject];
[[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) {
if ([[obj_ name] isEqualToString:query]) {
hasExactQueryMatch = YES;
*stop_ = YES;
}
}];
if (hasExactQueryMatch)
return NO;
sectionInfo = [[self.fetchedResultsControllerByLastUsed sections] lastObject];
[[sectionInfo objects] enumerateObjectsUsingBlock:^(id obj_, NSUInteger idx_, BOOL *stop_) {
if ([[obj_ name] isEqualToString:query]) {
hasExactQueryMatch = YES;
*stop_ = YES;
}
}];
if (hasExactQueryMatch)
return NO;
return YES;
}
- (void)customTableViewUpdates {
BOOL newSiteSectionIsNeeded = [self newSiteSectionNeeded];
if (newSiteSectionIsNeeded && !self.newSiteSectionWasNeeded)
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:2]
withRowAnimation:UITableViewRowAnimationAutomatic];
else if (!newSiteSectionIsNeeded && self.newSiteSectionWasNeeded)
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:2]
withRowAnimation:UITableViewRowAnimationAutomatic];
self.newSiteSectionWasNeeded = newSiteSectionIsNeeded;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger sectionCount = [super numberOfSectionsInTableView:tableView];
if ([self newSiteSectionNeeded])
++sectionCount;
return sectionCount;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections.
return [super tableView:tableView numberOfRowsInSection:section];
return 1;
}
- (void)configureCell:(UITableViewCell *)cell inTableView:(UITableView *)tableView atTableIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section < [super numberOfSectionsInTableView:tableView]) {
// Section is one of super's sections.
[super configureCell:cell inTableView:tableView atTableIndexPath:indexPath];
return;
}
// "New" section
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] );
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section < [super numberOfSectionsInTableView:tableView]) {
// Section is one of super's sections.
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
return;
}
// "New" section.
NSString *siteName
= [self.searchDisplayController.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[PearlAlert showAlertWithTitle:@"New Site"
message:PearlString( @"Do you want to create a new site named:\n%@", siteName )
viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (buttonIndex == [alert cancelButtonIndex])
return;
__weak MPElementListController *wSelf = self;
[[MPiOSAppDelegate get] addElementNamed:siteName completion:^(MPElementEntity *element) {
if (element)
PearlMainQueue( ^{
[wSelf.delegate didSelectElement:element];
} );
}];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections.
return [super tableView:tableView titleForHeaderInSection:section];
return @"Create";
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section < [super numberOfSectionsInTableView:tableView])
// Section is one of super's sections.
[super tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath];
}
@end

View File

@ -1,74 +0,0 @@
//
// MPMainViewController.h
// MasterPassword
//
// Created by Maarten Billemont on 24/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import <MessageUI/MessageUI.h>
#import "MPTypeViewController.h"
#import "MPElementListSearchController.h"
@interface MPMainViewController : UIViewController<MPTypeDelegate, UITextFieldDelegate, MPElementListDelegate, UIWebViewDelegate, UIGestureRecognizerDelegate>
@property(assign, nonatomic) BOOL siteInfoHidden;
@property(strong, nonatomic) IBOutlet MPElementListSearchController *searchDelegate;
@property(strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullDownGesture;
@property(strong, nonatomic) IBOutlet UIPanGestureRecognizer *pullUpGesture;
@property(weak, nonatomic) IBOutlet UITextField *contentField;
@property(weak, nonatomic) IBOutlet UIButton *typeButton;
@property(weak, nonatomic) IBOutlet UIWebView *helpView;
@property(weak, nonatomic) IBOutlet UILabel *siteName;
@property(weak, nonatomic) IBOutlet UILabel *passwordCounter;
@property(weak, nonatomic) IBOutlet UIButton *passwordIncrementer;
@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 *loginNameTipContainer;
@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 *loginNameTipBody;
@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 *loginNameContainer;
@property(weak, nonatomic) IBOutlet UITextField *loginNameField;
@property(weak, nonatomic) IBOutlet UIButton *passwordUser;
@property(weak, nonatomic) IBOutlet UIView *outdatedAlertContainer;
@property(weak, nonatomic) IBOutlet UIImageView *outdatedAlertBack;
@property(weak, nonatomic) IBOutlet UIButton *outdatedAlertCloseButton;
@property(weak, nonatomic) IBOutlet UIImageView *pullUpView;
@property(weak, nonatomic) IBOutlet UIImageView *pullDownView;
@property(copy, nonatomic) void (^contentTipCleanup)(BOOL finished);
@property(copy, nonatomic) void (^toolTipCleanup)(BOOL finished);
- (IBAction)copyContent;
- (IBAction)incrementPasswordCounter;
- (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender;
- (IBAction)editLoginName:(UILongPressGestureRecognizer *)sender;
- (IBAction)editPassword;
- (IBAction)closeAlert;
- (IBAction)upgradePassword;
- (IBAction)action:(UIBarButtonItem *)sender;
- (IBAction)toggleUser;
- (IBAction)searchOutdatedElements;
- (IBAction)closeOutdatedAlert;
- (IBAction)infoOutdatedAlert;
- (void)toggleHelpAnimated:(BOOL)animated;
- (void)setHelpHidden:(BOOL)hidden animated:(BOOL)animated;
- (void)setHelpChapter:(NSString *)chapter;
- (IBAction)panHelpDown:(UIPanGestureRecognizer *)sender;
- (IBAction)panHelpUp:(UIPanGestureRecognizer *)sender;
@end

View File

@ -1,887 +0,0 @@
//
// MPMainViewController.m
// MasterPassword
//
// Created by Maarten Billemont on 24/11/11.
// Copyright (c) 2011 Lyndir. All rights reserved.
//
#import "MPMainViewController.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPElementListAllViewController.h"
@interface MPMainViewController()
@property(nonatomic) BOOL suppressOutdatedAlert;
@end
@implementation MPMainViewController {
NSManagedObjectID *_activeElementOID;
}
#pragma mark - View lifecycle
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self updateHelpHiddenAnimated:NO];
[self updateUserHiddenAnimated:NO];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"MP_ChooseType"])
((MPTypeViewController *)[segue destinationViewController]).delegate = self;
if ([[segue identifier] isEqualToString:@"MP_AllSites"]) {
((MPElementListAllViewController *)[[segue destinationViewController] topViewController]).delegate = self;
((MPElementListAllViewController *)[[segue destinationViewController] topViewController]).filter = sender;
}
}
- (void)viewDidLoad {
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
self.alertBody.text = nil;
self.toolTipEditIcon.hidden = YES;
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:self queue:nil usingBlock:
^(NSNotification *note) {
self.suppressOutdatedAlert = NO;
}];
[[NSNotificationCenter defaultCenter] addObserverForName:MPElementUpdatedNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
MPElementEntity *activeElement = [self activeElementForMainThread];
if (activeElement.type & MPElementTypeClassStored &&
![[activeElement.algorithm resolveContentForElement:activeElement usingKey:[MPAppDelegate_Shared get].key] length])
[self showToolTip:@"Tap to set a password." withIcon:self.toolTipEditIcon];
if (activeElement.requiresExplicitMigration)
[self showToolTip:@"Password outdated. Tap to upgrade it." withIcon:nil];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
BOOL animated = [(note.userInfo)[@"animated"] boolValue];
_activeElementOID = nil;
self.suppressOutdatedAlert = NO;
[self updateAnimated:NO];
[[[PearlSheet activeSheets] copy] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[obj cancelSheetAnimated:NO];
}];
if (![self.navigationController presentedViewController])
[self.navigationController popToRootViewControllerAnimated:animated];
else
[self.navigationController dismissViewControllerAnimated:animated completion:^{
[self.navigationController popToRootViewControllerAnimated:animated];
}];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
if (!self.activeElementForMainThread)
[self didSelectElement:nil];
}];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
if (![super respondsToSelector:@selector(prefersStatusBarHidden)])
[UIApp setStatusBarHidden:NO withAnimation:animated? UIStatusBarAnimationSlide: UIStatusBarAnimationNone];
[self.navigationController setNavigationBarHidden:NO animated:animated];
MPElementEntity *activeElement = [self activeElementForMainThread];
if (activeElement.user != [[MPiOSAppDelegate get] activeUserForMainThread])
_activeElementOID = nil;
self.searchDisplayController.searchBar.text = nil;
self.alertContainer.hidden = NO;
self.outdatedAlertContainer.hidden = NO;
self.searchTipContainer.hidden = NO;
self.actionsTipContainer.hidden = NO;
self.typeTipContainer.hidden = NO;
self.toolTipContainer.hidden = NO;
self.contentTipContainer.hidden = NO;
self.loginNameTipContainer.hidden = NO;
[self updateAnimated:NO];
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
inf(@"Main will appear");
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
if ([MPAlgorithmDefault migrateUser:activeUser inContext:moc] && !self.suppressOutdatedAlert)
[UIView animateWithDuration:0.3f animations:^{
self.outdatedAlertContainer.alpha = 1;
self.suppressOutdatedAlert = YES;
}];
[moc saveToStore];
}];
if (![[MPiOSConfig get].actionsTipShown boolValue])
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
self.actionsTipContainer.alpha = 1;
} completion:^(BOOL finished) {
if (!finished)
return;
[MPiOSConfig get].actionsTipShown = @YES;
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.2f animations:^{
self.actionsTipContainer.alpha = 0;
} completion:^(BOOL finished_) {
if (!_activeElementOID)
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
self.searchTipContainer.alpha = 1;
}];
}];
} );
}];
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
inf(@"Main will disappear.");
[super viewWillDisappear:animated];
}
- (void)updateAnimated:(BOOL)animated {
if (animated) {
[UIView animateWithDuration:0.3f animations:^{
[self updateAnimated:NO];
}];
return;
}
MPElementEntity *activeElement = [self activeElementForMainThread];
[self setHelpChapter:activeElement? @"2": @"1"];
[self updateHelpHiddenAnimated:NO];
self.passwordCounter.alpha = 0;
self.passwordIncrementer.alpha = 0;
self.passwordEdit.alpha = 0;
self.passwordUpgrade.alpha = 0;
self.passwordUser.alpha = 0;
self.displayContainer.alpha = 0;
if (activeElement) {
self.passwordUser.alpha = 0.5f;
self.displayContainer.alpha = 1.0f;
}
if (activeElement.requiresExplicitMigration)
self.passwordUpgrade.alpha = 0.5f;
else {
if (activeElement.type & MPElementTypeClassGenerated) {
self.passwordCounter.alpha = 0.5f;
self.passwordIncrementer.alpha = 0.5f;
}
else if (activeElement.type & MPElementTypeClassStored)
self.passwordEdit.alpha = 0.5f;
}
self.siteName.text = activeElement.name;
self.typeButton.alpha = activeElement? 1: 0;
[self.typeButton setTitle:activeElement.typeName
forState:UIControlStateNormal];
if ([activeElement isKindOfClass:[MPElementGeneratedEntity class]])
self.passwordCounter.text = PearlString( @"%lu", (unsigned long)((MPElementGeneratedEntity *)activeElement).counter );
self.contentField.enabled = NO;
self.contentField.text = @"";
if (activeElement.name && ![activeElement isDeleted])
[activeElement.algorithm resolveContentForElement:activeElement usingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
dispatch_async( dispatch_get_main_queue(), ^{
self.contentField.text = result;
} );
}];
self.loginNameField.enabled = NO;
self.loginNameField.text = activeElement.loginName;
self.siteInfoHidden = !activeElement || ([[MPiOSConfig get].siteInfoHidden boolValue] && (activeElement.loginName == nil));
[self updateUserHiddenAnimated:NO];
}
- (void)toggleHelpAnimated:(BOOL)animated {
[self setHelpHidden:![[MPiOSConfig get].helpHidden boolValue] animated:animated];
}
- (void)setHelpHidden:(BOOL)hidden animated:(BOOL)animated {
[MPiOSConfig get].helpHidden = @(hidden);
[self updateHelpHiddenAnimated:animated];
}
- (void)updateHelpHiddenAnimated:(BOOL)animated {
if (animated) {
[UIView animateWithDuration:0.3f animations:^{
[self updateHelpHiddenAnimated:NO];
}];
return;
}
self.pullUpView.hidden = ![[MPiOSConfig get].helpHidden boolValue];
self.pullDownView.hidden = [[MPiOSConfig get].helpHidden boolValue];
if ([[MPiOSConfig get].helpHidden boolValue]) {
self.contentContainer.frame = CGRectSetHeight( self.contentContainer.frame, self.view.bounds.size.height - 44 /* search bar */);
self.helpContainer.frame = CGRectSetY( self.helpContainer.frame, self.view.bounds.size.height - 20 /* pull-up */);
}
else {
self.contentContainer.frame = CGRectSetHeight( self.contentContainer.frame, 225 );
[self.helpContainer setFrameFromCurrentSizeAndParentPaddingTop:CGFLOAT_MAX right:0 bottom:0 left:0];
}
}
- (IBAction)toggleUser {
[self toggleUserAnimated:YES];
}
- (void)toggleUserAnimated:(BOOL)animated {
[MPiOSConfig get].siteInfoHidden = @(!self.siteInfoHidden);
self.siteInfoHidden = [[MPiOSConfig get].siteInfoHidden boolValue];
[self updateUserHiddenAnimated:animated];
}
- (void)updateUserHiddenAnimated:(BOOL)animated {
if (animated) {
[UIView animateWithDuration:0.3f animations:^{
[self updateUserHiddenAnimated:NO];
}];
return;
}
if (self.siteInfoHidden) {
self.displayContainer.frame = CGRectSetHeight( self.displayContainer.frame, 87 );
}
else {
self.displayContainer.frame = CGRectSetHeight( self.displayContainer.frame, 137 );
}
}
- (void)setHelpChapter:(NSString *)chapter {
MPCheckpoint( MPCheckpointHelpChapter, @{
@"chapter" : NilToNSNull(chapter)
} );
dispatch_async( dispatch_get_main_queue(), ^{
NSURL *url = [NSURL URLWithString:[@"#" stringByAppendingString:chapter]
relativeToURL:[[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]];
[self.helpView loadRequest:[NSURLRequest requestWithURL:url]];
} );
}
- (IBAction)panHelpDown:(UIPanGestureRecognizer *)sender {
CGFloat targetY = MIN(self.view.bounds.size.height - 20, 246 + [sender translationInView:self.helpContainer].y);
BOOL hideHelp = YES;
if (targetY <= 246) {
hideHelp = NO;
targetY = 246;
}
self.helpContainer.frame = CGRectSetY( self.helpContainer.frame, targetY );
if (sender.state == UIGestureRecognizerStateEnded)
[self setHelpHidden:hideHelp animated:YES];
}
- (IBAction)panHelpUp:(UIPanGestureRecognizer *)sender {
CGFloat targetY = MAX(246, self.view.bounds.size.height - 20 + [sender translationInView:self.helpContainer].y);
BOOL hideHelp = NO;
if (targetY >= self.view.bounds.size.height - 20) {
hideHelp = YES;
targetY = self.view.bounds.size.height - 20;
}
self.helpContainer.frame = CGRectSetY( self.helpContainer.frame, targetY );
if (sender.state == UIGestureRecognizerStateEnded)
[self setHelpHidden:hideHelp animated:YES];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
MPElementEntity *activeElement = [self activeElementForMainThread];
NSString *error = [self.helpView stringByEvaluatingJavaScriptFromString:
PearlString( @"setClass('%@');", activeElement.typeClassName )];
if (error.length)
err(@"helpView.setClass: %@", error);
}
- (void)showContentTip:(NSString *)message withIcon:(UIImageView *)icon {
dispatch_async( dispatch_get_main_queue(), ^{
if (self.contentTipCleanup)
self.contentTipCleanup( NO );
__weak MPMainViewController *wSelf = self;
self.contentTipBody.text = message;
self.contentTipCleanup = ^(BOOL finished) {
icon.hidden = YES;
wSelf.contentTipCleanup = nil;
};
icon.hidden = NO;
[UIView animateWithDuration:0.3f animations:^{
self.contentTipContainer.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.contentTipContainer.alpha = 0;
} completion:self.contentTipCleanup];
} );
}
}];
} );
}
- (void)showLoginNameTip:(NSString *)message {
dispatch_async( dispatch_get_main_queue(), ^{
self.loginNameTipBody.text = message;
[UIView animateWithDuration:0.3f animations:^{
self.loginNameTipContainer.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.loginNameTipContainer.alpha = 0;
}];
} );
}
}];
} );
}
- (void)showToolTip:(NSString *)message withIcon:(UIImageView *)icon {
dispatch_async( dispatch_get_main_queue(), ^{
if (self.toolTipCleanup)
self.toolTipCleanup( NO );
__weak MPMainViewController *wSelf = self;
self.toolTipBody.text = message;
self.toolTipCleanup = ^(BOOL finished) {
icon.hidden = YES;
wSelf.toolTipCleanup = nil;
};
icon.hidden = NO;
[UIView animateWithDuration:0.3f animations:^{
self.toolTipContainer.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.toolTipContainer.alpha = 0;
} completion:self.toolTipCleanup];
} );
}
}];
} );
}
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
dispatch_async( dispatch_get_main_queue(), ^{
self.alertTitle.text = title;
NSRange scrollRange = NSMakeRange( self.alertBody.text.length, message.length );
if ([self.alertBody.text length])
self.alertBody.text = [NSString stringWithFormat:@"%@\n\n---\n\n%@", self.alertBody.text, message];
else
self.alertBody.text = message;
[self.alertBody scrollRangeToVisible:scrollRange];
[UIView animateWithDuration:0.3f animations:^{
self.alertContainer.alpha = 1;
}];
} );
}
#pragma mark - Protocols
- (IBAction)copyContent {
MPElementEntity *activeElement = [self activeElementForMainThread];
inf(@"Copying password for: %@", activeElement.name);
MPCheckpoint( MPCheckpointCopyToPasteboard, @{
@"type" : NilToNSNull(activeElement.typeName),
@"version" : @(activeElement.version),
@"emergency" : @NO
} );
[activeElement.algorithm resolveContentForElement:activeElement usingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
if (!result)
// Nothing to copy.
return;
[UIPasteboard generalPasteboard].string = result;
[self showContentTip:@"Copied!" withIcon:nil];
}];
}
- (IBAction)copyLoginName:(UITapGestureRecognizer *)sender {
MPElementEntity *activeElement = [self activeElementForMainThread];
if (!activeElement.loginName)
return;
inf(@"Copying user name for: %@", activeElement.name);
[UIPasteboard generalPasteboard].string = activeElement.loginName;
[self showLoginNameTip:@"Copied!"];
MPCheckpoint( MPCheckpointCopyLoginNameToPasteboard, @{
@"type" : NilToNSNull(activeElement.typeName),
@"version" : @(activeElement.version)
} );
}
- (IBAction)incrementPasswordCounter {
[self changeActiveElementWithWarning:
@"You are incrementing the site's password counter.\n\n"
@"If you continue, a new password will be generated for this site. "
@"You will then need to update your account's old password to this newly generated password.\n\n"
@"You can reset the counter by holding down on this button."
do:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
if (![activeElement isKindOfClass:[MPElementGeneratedEntity class]]) {
// Not of a type that supports a password counter.
err(@"Cannot increment password counter: Element is not generated: %@", activeElement.name);
return NO;
}
MPElementGeneratedEntity *activeGeneratedElement = (MPElementGeneratedEntity *)activeElement;
inf(@"Incrementing password counter for: %@", activeGeneratedElement.name);
++activeGeneratedElement.counter;
MPCheckpoint( MPCheckpointIncrementPasswordCounter, @{
@"type" : NilToNSNull(activeGeneratedElement.typeName),
@"version" : @(activeGeneratedElement.version),
@"counter" : @(activeGeneratedElement.counter)
} );
return YES;
}];
}
- (IBAction)resetPasswordCounter:(UILongPressGestureRecognizer *)sender {
if (sender.state != UIGestureRecognizerStateBegan)
// Only fire when the gesture was first detected.
return;
MPElementEntity *activeElement = [self activeElementForMainThread];
if (![activeElement isKindOfClass:[MPElementGeneratedEntity class]]) {
// Not of a type that supports a password counter.
err(@"Cannot reset password counter: Element is not generated: %@", activeElement.name);
return;
}
else if (((MPElementGeneratedEntity *)activeElement).counter == 1)
// Counter has initial value, no point resetting.
return;
[self changeActiveElementWithWarning:
@"You are resetting the site's password counter.\n\n"
@"If you continue, the site's password will change back to its original value. "
@"You will then need to update your account's password back to this original value."
do:^BOOL(MPElementEntity *activeElement_, NSManagedObjectContext *context) {
inf(@"Resetting password counter for: %@", activeElement_.name);
((MPElementGeneratedEntity *)activeElement_).counter = 1;
MPCheckpoint( MPCheckpointResetPasswordCounter, @{
@"type" : NilToNSNull(activeElement_.typeName),
@"version" : @(activeElement_.version)
} );
return YES;
}];
}
- (IBAction)editLoginName:(UILongPressGestureRecognizer *)sender {
if (sender.state != UIGestureRecognizerStateBegan)
// Only fire when the gesture was first detected.
return;
MPElementEntity *activeElement = [self activeElementForMainThread];
if (!activeElement)
return;
self.loginNameField.enabled = YES;
[self.loginNameField becomeFirstResponder];
MPCheckpoint( MPCheckpointEditLoginName, @{
@"type" : NilToNSNull(activeElement.typeName),
@"version" : @(activeElement.version)
} );
}
- (void)changeActiveElementWithWarning:(NSString *)warning
do:(BOOL (^)(MPElementEntity *activeElement, NSManagedObjectContext *context))task {
[PearlAlert showAlertWithTitle:@"Password Change" message:warning viewStyle:UIAlertViewStyleDefault
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex == [alert cancelButtonIndex])
return;
[self changeActiveElementWithoutWarningDo:task];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonContinue, nil];
}
- (void)changeActiveElementWithoutWarningDo:(BOOL (^)(MPElementEntity *, NSManagedObjectContext *context))task {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPElementEntity *activeElement = [self activeElementInContext:context];
if (!activeElement)
return;
MPKey *key = [MPAppDelegate_Shared get].key;
NSString *oldPassword = [activeElement.algorithm resolveContentForElement:activeElement usingKey:key];
if (!task( activeElement, context ))
return;
activeElement = [self activeElementInContext:context];
NSString *newPassword = [activeElement.algorithm resolveContentForElement:activeElement usingKey:key];
// Save.
[context saveToStore];
// Update the UI.
dispatch_async( dispatch_get_main_queue(), ^{
[self updateAnimated:YES];
// Show new and old password.
if ([oldPassword length] && ![oldPassword isEqualToString:newPassword])
[self showAlertWithTitle:@"Password Changed!"
message:PearlString( @"The password for %@ has changed.\n\n"
@"IMPORTANT:\n"
@"Don't forget to update the site with your new password! "
@"Your old password was:\n"
@"%@", activeElement.name, oldPassword )];
} );
}];
}
- (MPElementEntity *)activeElementForMainThread {
return [self activeElementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
}
- (MPElementEntity *)activeElementInContext:(NSManagedObjectContext *)moc {
if (!_activeElementOID)
return nil;
NSError *error;
MPElementEntity *activeElement = (MPElementEntity *)[moc existingObjectWithID:_activeElementOID error:&error];
if (!activeElement)
err(@"Couldn't retrieve active element: %@", error);
return activeElement;
}
- (IBAction)editPassword {
MPElementEntity *activeElement = [self activeElementForMainThread];
if (!(activeElement.type & MPElementTypeClassStored)) {
// Not of a type that supports editing the content.
err(@"Cannot edit content: Element is not stored: %@", activeElement.name);
return;
}
self.contentField.enabled = YES;
[self.contentField becomeFirstResponder];
MPCheckpoint( MPCheckpointEditPassword, @{
@"type" : NilToNSNull(activeElement.typeName),
@"version" : @(activeElement.version)
} );
}
- (IBAction)upgradePassword {
MPElementEntity *activeElement = [self activeElementForMainThread];
if (!activeElement)
return;
NSString *warning = activeElement.type & MPElementTypeClassGenerated?
@"You are upgrading the site.\n\n"
@"This upgrade improves the site's compatibility with the latest version of Master Password.\n\n"
@"Your password will change and you will need to update your site's account."
:
@"You are upgrading the site.\n\n"
@"This upgrade improves the site's compatibility with the latest version of Master Password.";
[self changeActiveElementWithWarning:warning do:
^BOOL(MPElementEntity *activeElement_, NSManagedObjectContext *context) {
inf(@"Explicitly migrating element: %@", activeElement_);
[activeElement_ migrateExplicitly:YES];
MPCheckpoint( MPCheckpointExplicitMigration, @{
@"type" : NilToNSNull(activeElement_.typeName),
@"version" : @(activeElement_.version)
} );
return YES;
}];
}
- (IBAction)searchOutdatedElements {
[self performSegueWithIdentifier:@"MP_AllSites" sender:MPElementListFilterOutdated];
}
- (IBAction)closeAlert {
[UIView animateWithDuration:0.3f animations:^{
self.alertContainer.alpha = 0;
} completion:^(BOOL finished) {
if (finished)
self.alertBody.text = nil;
}];
}
- (IBAction)closeOutdatedAlert {
[UIView animateWithDuration:0.3f animations:^{
self.outdatedAlertContainer.alpha = 0;
}];
}
- (IBAction)infoOutdatedAlert {
[self setHelpChapter:@"outdated"];
[self setHelpHidden:NO animated:YES];
[self closeOutdatedAlert];
self.suppressOutdatedAlert = NO;
}
- (IBAction)action:(id)sender {
[PearlSheet showSheetWithTitle:nil viewStyle:UIActionSheetStyleAutomatic
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex])
return;
switch (buttonIndex - [sheet firstOtherButtonIndex]) {
case 0: {
inf(@"Action: FAQ");
[self setHelpChapter:@"faq"];
[self setHelpHidden:NO animated:YES];
break;
}
case 2: {
inf(@"Action: Preferences");
[self performSegueWithIdentifier:@"MP_UserProfile" sender:self];
break;
}
case 3: {
inf(@"Action: Other Apps");
[self performSegueWithIdentifier:@"MP_OtherApps" sender:self];
break;
}
//#if defined(ADHOC) && defined(TESTFLIGHT_SDK_VERSION)
// case 4: {
// inf(@"Action: Feedback via TestFlight");
// [TestFlight openFeedbackView];
// break;
// }
//#else
case 4: {
inf(@"Action: Feedback via Mail");
[[MPiOSAppDelegate get] showFeedbackWithLogs:YES forVC:self];
break;
}
//#endif
default: {
wrn(@"Unsupported action: %ld", (long)(buttonIndex - [sheet firstOtherButtonIndex]));
break;
}
}
}
cancelTitle:[PearlStrings get].commonButtonCancel destructiveTitle:nil otherTitles:
@"FAQ",
@"Overview",
@"User Profile",
@"Other Apps",
@"Feedback",
nil];
}
- (MPElementType)selectedType {
return [self selectedElement].type;
}
- (MPElementEntity *)selectedElement {
return [self activeElementForMainThread];
}
- (void)didSelectType:(MPElementType)type {
[self changeActiveElementWithWarning:
@"You are about to change the type of this password.\n\n"
@"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 saveInContext:context
toType:type].objectID;
return YES;
}];
}
- (void)didSelectElement:(MPElementEntity *)element {
inf(@"Selected: %@", element.name);
_activeElementOID = element.objectID;
[self closeAlert];
if (element) {
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
if ([activeElement use] == 1)
[self showAlertWithTitle:@"New Site" message:
PearlString( @"You've just created a password for %@.\n\n"
@"IMPORTANT:\n"
@"Go to %@ and set or change the password for your account to the password above.\n"
@"Do this right away: if you forget, you may have trouble remembering which password to use to log into the site later on.",
activeElement.name, activeElement.name )];
return YES;
}];
if (![[MPiOSConfig get].typeTipShown boolValue])
[UIView animateWithDuration:0.5f animations:^{
self.typeTipContainer.alpha = 1;
} completion:^(BOOL finished) {
if (finished) {
[MPiOSConfig get].typeTipShown = PearlBool(YES);
dispatch_after(
dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.2f animations:^{
self.typeTipContainer.alpha = 0;
}];
} );
}
}];
MPCheckpoint( MPCheckpointUseType, @{
@"type" : NilToNSNull(element.typeName),
@"version" : @(element.version)
} );
}
[self.searchDisplayController setActive:NO animated:YES];
self.searchDisplayController.searchBar.text = element.name;
[self updateAnimated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == self.contentField)
[self.contentField resignFirstResponder];
if (textField == self.loginNameField)
[self.loginNameField resignFirstResponder];
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
if (textField == self.contentField) {
self.contentField.enabled = NO;
MPElementEntity *activeElement = [self activeElementForMainThread];
MPKey *key = [MPAppDelegate_Shared get].key;
if (![activeElement isKindOfClass:[MPElementStoredEntity class]]) {
// Not of a type whose content can be edited.
err(@"Cannot update element content: Element is not stored: %@", activeElement.name);
return;
}
else if ([[activeElement.algorithm resolveContentForElement:activeElement usingKey:key] isEqual:self.contentField.text])
// Content hasn't changed.
return;
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement_, NSManagedObjectContext *context) {
[activeElement_.algorithm saveContent:self.contentField.text toElement:activeElement_ usingKey:key];
return YES;
}];
}
if (textField == self.loginNameField) {
self.loginNameField.enabled = NO;
if (![[MPiOSConfig get].loginNameTipShown boolValue]) {
[self showLoginNameTip:@"Tap to copy or hold to edit."];
[MPiOSConfig get].loginNameTipShown = @YES;
}
[self changeActiveElementWithoutWarningDo:^BOOL(MPElementEntity *activeElement, NSManagedObjectContext *context) {
if ([self.loginNameField.text length])
activeElement.loginName = self.loginNameField.text;
else
activeElement.loginName = nil;
return YES;
}];
}
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
if ([[[request URL] query] isEqualToString:@"outdated"]) {
[self searchOutdatedElements];
return NO;
}
[UIApp openURL:[request URL]];
return NO;
}
return YES;
}
@end

View File

@ -9,17 +9,15 @@
*/
//
// MPAppViewController
// MPNavigationController.h
// MPNavigationController
//
// Created by Maarten Billemont on 2012-08-31.
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
// Created by lhunath on 2014-06-03.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MPAppViewController : UIViewController
- (IBAction)gorillas:(UIButton *)sender;
- (IBAction)deblock:(UIButton *)sender;
@interface MPNavigationController : UINavigationController
@end

View File

@ -0,0 +1,30 @@
/**
* 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
*/
//
// MPNavigationController.h
// MPNavigationController
//
// Created by lhunath on 2014-06-03.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPNavigationController.h"
#import "MPWebViewController.h"
@implementation MPNavigationController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"web"])
((MPWebViewController *)segue.destinationViewController).initialURL = [NSURL URLWithString:@"http://thanks.lhunath.com"];
}
@end

View File

@ -45,6 +45,7 @@ typedef NS_ENUM (NSUInteger, MPContentFieldMode) {
- (void)resolveContentOfCellTypeForTransientSite:(NSString *)siteName usingKey:(MPKey *)key result:(void (^)(NSString *))resultBlock;
- (void)resolveContentOfCellTypeForElement:(MPElementEntity *)element usingKey:(MPKey *)key result:(void (^)(NSString *))resultBlock;
- (void)willBeginDragging;
- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context;
@end

View File

@ -152,6 +152,9 @@
resultBlock( nil );
}
- (void)willBeginDragging {
}
- (MPElementEntity *)saveContentTypeWithElement:(MPElementEntity *)element saveInContext:(NSManagedObjectContext *)context {
return [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:self.type];

View File

@ -21,6 +21,7 @@
@interface MPPasswordLargeGeneratedCell : MPPasswordLargeCell
@property(strong, nonatomic) IBOutlet UILabel *strengthLabel;
@property(strong, nonatomic) IBOutlet UILabel *counterLabel;
@property(strong, nonatomic) IBOutlet UIButton *counterButton;

View File

@ -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 <lhunath@lyndir.com>
* @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 <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPPasswordLargeGeneratedCell.h
@ -21,6 +21,11 @@
#import "MPAppDelegate_Store.h"
#import "MPPasswordTypesCell.h"
@interface MPPasswordLargeGeneratedCell()
@property(nonatomic, weak) NSTimer *hideStrengthTimer;
@end
@implementation MPPasswordLargeGeneratedCell
- (void)awakeFromNib {
@ -28,10 +33,40 @@
[super awakeFromNib];
UILongPressGestureRecognizer *gestureRecognizer = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(doResetCounterRecognizer:)];
initWithTarget:self action:@selector( doResetCounterRecognizer: )];
[self.counterButton addGestureRecognizer:gestureRecognizer];
}
- (void)prepareForReuse {
[super prepareForReuse];
self.strengthLabel.alpha = 0;
}
- (void)willBeginDragging {
[super willBeginDragging];
[UIView animateWithDuration:0.3f animations:^{
self.strengthLabel.alpha = 1;
}];
[self.hideStrengthTimer invalidate];
self.hideStrengthTimer = [NSTimer scheduledTimerWithTimeInterval:1 block:^(NSTimer *timer) {
[UIView animateWithDuration:0.3f animations:^{
self.strengthLabel.alpha = 0;
}];
} repeats:NO];
}
- (void)update {
[super update];
self.counterLabel.alpha = self.counterButton.alpha = 0;
}
- (void)updateWithElement:(MPElementEntity *)mainElement {
[super updateWithElement:mainElement];
@ -42,24 +77,18 @@
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;
}
if (mainElement && !mainElement.requiresExplicitMigration)
self.counterLabel.alpha = self.counterButton.alpha = 1;
}
- (void)resolveContentOfCellTypeForTransientSite:(NSString *)siteName usingKey:(MPKey *)key result:(void (^)(NSString *))resultBlock {
- (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 {
- (void)resolveContentOfCellTypeForElement:(MPElementEntity *)element usingKey:(MPKey *)key result:(void ( ^ )(NSString *))resultBlock {
id<MPAlgorithm> algorithm = element.algorithm;
NSString *siteName = element.name;
@ -103,7 +132,7 @@
- (void)doResetCounterRecognizer:(UILongPressGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
if (gestureRecognizer.state != UIGestureRecognizerStateRecognized)
return;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
@ -123,6 +152,38 @@
#pragma mark - Properties
- (void)setType:(MPElementType)type {
[super setType:type];
switch (type) {
case MPElementTypeGeneratedMaximum:
self.strengthLabel.text = @"> age of the universe";
break;
case MPElementTypeGeneratedLong:
self.strengthLabel.text = @"196 billion years";
break;
case MPElementTypeGeneratedMedium:
self.strengthLabel.text = @"5 months";
break;
case MPElementTypeGeneratedBasic:
self.strengthLabel.text = @"12 days";
break;
case MPElementTypeGeneratedShort:
self.strengthLabel.text = @"trivial";
break;
case MPElementTypeGeneratedPIN:
self.strengthLabel.text = @"trivial";
break;
case MPElementTypeStoredPersonal:
self.strengthLabel.text = @"";
break;
case MPElementTypeStoredDevicePrivate:
self.strengthLabel.text = @"";
break;
}
}
- (MPElementGeneratedEntity *)generatedElementInContext:(NSManagedObjectContext *)context {
return [self generatedElement:[[MPPasswordTypesCell findAsSuperviewOf:self] elementInContext:context]];

View File

@ -24,6 +24,7 @@
@implementation MPPasswordTypesCell {
NSManagedObjectID *_elementOID;
BOOL _scrolling;
}
#pragma mark - Lifecycle
@ -110,7 +111,9 @@
[cell updateWithTransientSite:self.transientSite];
else
[cell updateWithElement:self.mainElement];
dbg( @"cell %d, contentFieldMode: %d", indexPath.item, cell.contentFieldMode );
if (_scrolling)
[cell willBeginDragging];
return cell;
}
@ -189,6 +192,13 @@
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
_scrolling = YES;
for (MPPasswordLargeCell *cell in [self.contentCollectionView visibleCells])
[cell willBeginDragging];
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset {
@ -202,12 +212,14 @@
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
_scrolling = NO;
if (scrollView == self.contentCollectionView && !decelerate)
[self saveContentType];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
_scrolling = NO;
if (scrollView == self.contentCollectionView)
[self saveContentType];
}

View File

@ -408,11 +408,8 @@ referenceSizeForHeaderInSection:(NSInteger)section {
_active = active;
[UIView animateWithDuration:animated? 0.4f: 0 animations:^{
self.navigationBarToTopConstraint.priority = active? 1: UILayoutPriorityDefaultHigh;
self.passwordsToBottomConstraint.priority = active? 1: UILayoutPriorityDefaultHigh;
[self.navigationBarToTopConstraint apply];
[self.passwordsToBottomConstraint apply];
[self.navigationBarToTopConstraint layoutWithPriority:active? 1: UILayoutPriorityDefaultHigh];
[self.passwordsToBottomConstraint layoutWithPriority:active? 1: UILayoutPriorityDefaultHigh];
} completion:completion];
}

View File

@ -38,9 +38,8 @@
metrics:nil views:NSDictionaryOfVariableBindings(popdownView)];
[UIView animateWithDuration:0.3f animations:^{
passwordsVC.popdownToTopConstraint.priority = 1;
[passwordsVC.popdownToTopConstraint apply];
} completion:^(BOOL finished) {
[passwordsVC.popdownToTopConstraint layoutWithPriority:1];
} completion:^(BOOL finished) {
if (finished)
[popdownVC didMoveToParentViewController:passwordsVC];
}];
@ -51,8 +50,7 @@
[popdownVC willMoveToParentViewController:nil];
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionOverrideInheritedDuration animations:^{
passwordsVC.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh;
[passwordsVC.popdownToTopConstraint apply];
[passwordsVC.popdownToTopConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
} completion:^(BOOL finished) {
if (finished) {
[popdownVC.view removeFromSuperview];

View File

@ -24,5 +24,9 @@
- (IBAction)previousAvatar:(id)sender;
- (IBAction)nextAvatar:(id)sender;
- (IBAction)valueChanged:(id)sender;
- (IBAction)homePageButton:(id)sender;
- (IBAction)securityButton:(id)sender;
- (IBAction)sourceButton:(id)sender;
- (IBAction)thanksButton:(id)sender;
@end

View File

@ -142,6 +142,30 @@
}];
}
- (IBAction)homePageButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"http://masterpasswordapp.com"]];
}
- (IBAction)securityButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"http://masterpasswordapp.com/security.html"]];
}
- (IBAction)sourceButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"https://github.com/Lyndir/MasterPassword/"]];
}
- (IBAction)thanksButton:(id)sender {
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
[NSURL URLWithString:@"http://thanks.lhunath.com"]];
}
#pragma mark - Private
- (MPPasswordsViewController *)dismissPopup {

View File

@ -1,23 +0,0 @@
//
// MPPreferencesViewController.h
// MasterPassword-iOS
//
// Created by Maarten Billemont on 04/06/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "MPTypeViewController.h"
@interface MPPreferencesViewControllerOld : UITableViewController<MPTypeDelegate>
@property(weak, nonatomic) IBOutlet UIScrollView *avatarsView;
@property(weak, nonatomic) IBOutlet UIButton *avatarTemplate;
@property(weak, nonatomic) IBOutlet UISwitch *savePasswordSwitch;
@property(weak, nonatomic) IBOutlet UITableViewCell *exportCell;
@property(weak, nonatomic) IBOutlet UITableViewCell *changeMPCell;
@property(weak, nonatomic) IBOutlet UILabel *defaultTypeLabel;
- (IBAction)didToggleSwitch:(UISwitch *)sender;
@end

View File

@ -1,163 +0,0 @@
//
// MPPreferencesViewController.m
// MasterPassword-iOS
//
// Created by Maarten Billemont on 04/06/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import "MPPreferencesViewControllerOld.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
@interface MPPreferencesViewControllerOld()
@end
@implementation MPPreferencesViewControllerOld
- (void)viewDidLoad {
self.avatarTemplate.hidden = YES;
for (NSUInteger a = 0; a < MPAvatarCount; ++a) {
UIButton *avatar = [self.avatarTemplate clone];
avatar.tag = a;
avatar.hidden = NO;
avatar.center = CGPointMake(
self.avatarTemplate.center.x * (a + 1) + self.avatarTemplate.bounds.size.width / 2 * a,
self.avatarTemplate.center.y );
[avatar setBackgroundImage:[UIImage imageNamed:PearlString( @"avatar-%ld", (long)a )]
forState:UIControlStateNormal];
[avatar setSelectionInSuperviewCandidate:YES isClearable:NO];
avatar.layer.cornerRadius = avatar.bounds.size.height / 2;
avatar.layer.shadowColor = [UIColor blackColor].CGColor;
avatar.layer.shadowOpacity = 1;
avatar.layer.shadowRadius = 5;
avatar.backgroundColor = [UIColor clearColor];
[avatar onHighlightOrSelect:^(BOOL highlighted, BOOL selected) {
if (highlighted || selected)
avatar.backgroundColor = self.avatarTemplate.backgroundColor;
else
avatar.backgroundColor = [UIColor clearColor];
} options:0];
[avatar onSelect:^(BOOL selected) {
if (selected) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
[[MPiOSAppDelegate get] activeUserInContext:moc].avatar = (unsigned)avatar.tag;
[moc saveToStore];
}];
}
} options:0];
avatar.selected = (a == [[MPiOSAppDelegate get] activeUserForMainThread].avatar);
}
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
inf(@"Preferences will appear");
[self.avatarsView autoSizeContent];
[self.avatarsView enumerateViews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
if (subview.tag && ((UIControl *)subview).selected) {
[self.avatarsView setContentOffset:CGPointMake( subview.center.x - self.avatarsView.bounds.size.width / 2, 0 )
animated:animated];
}
} recurse:NO];
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread];
self.savePasswordSwitch.on = activeUser.saveKey;
self.defaultTypeLabel.text = [[MPiOSAppDelegate get].key.algorithm shortNameOfType:activeUser.defaultType];
[super viewWillAppear:animated];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (motion == UIEventSubtypeMotionShake) {
MPCheckpoint( MPCheckpointLogs, @{
@"trace" : [MPiOSConfig get].traceMode
} );
[self performSegueWithIdentifier:@"MP_Logs" sender:self];
}
}
- (BOOL)shouldAutorotate {
return NO;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"MP_ChooseType"])
((MPTypeViewController *)[segue destinationViewController]).delegate = self;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell == self.exportCell)
[[MPiOSAppDelegate get] showExportForVC:self];
else if (cell == self.changeMPCell) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
[[MPiOSAppDelegate get] changeMasterPasswordFor:activeUser saveInContext:moc didResetBlock:nil];
}];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - MPTypeDelegate
- (void)didSelectType:(MPElementType)type {
self.defaultTypeLabel.text = [[MPiOSAppDelegate get].key.algorithm shortNameOfType:type];
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:context];
activeUser.defaultType = type;
[context saveToStore];
}];
}
- (MPElementType)selectedType {
return [[MPiOSAppDelegate get] activeUserForMainThread].defaultType;
}
#pragma mark - IBActions
- (IBAction)didToggleSwitch:(UISwitch *)sender {
if (sender == self.savePasswordSwitch)
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:moc];
if ((activeUser.saveKey = sender.on))
[[MPiOSAppDelegate get] storeSavedKeyFor:activeUser];
else
[[MPiOSAppDelegate get] forgetSavedKeyFor:activeUser];
[moc saveToStore];
}];
}
@end

View File

@ -1,54 +0,0 @@
//
// MBUnlockViewController.h
// MasterPassword
//
// Created by Maarten Billemont on 22/02/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "LLGitTip.h"
@interface MPUnlockViewController : UIViewController<UITextFieldDelegate, UIScrollViewDelegate, UIWebViewDelegate>
@property(weak, nonatomic) IBOutlet UIImageView *spinner;
@property(weak, nonatomic) IBOutlet UILabel *passwordFieldLabel;
@property(weak, nonatomic) IBOutlet UITextField *passwordField;
@property(weak, nonatomic) IBOutlet UIView *passwordView;
@property(weak, nonatomic) IBOutlet UIScrollView *avatarsView;
@property(weak, nonatomic) IBOutlet UILabel *nameLabel;
@property(weak, nonatomic) IBOutlet UILabel *oldNameLabel;
@property(weak, nonatomic) IBOutlet UIButton *avatarTemplate;
@property(weak, nonatomic) IBOutlet UIView *createPasswordTipView;
@property(weak, nonatomic) IBOutlet UILabel *tip;
@property(weak, nonatomic) IBOutlet UIView *passwordTipView;
@property(weak, nonatomic) IBOutlet UILabel *passwordTipLabel;
@property(weak, nonatomic) IBOutlet UIView *wordWall;
@property(strong, nonatomic) IBOutlet UILongPressGestureRecognizer *targetedUserActionGesture;
@property(weak, nonatomic) IBOutlet UIView *uiContainer;
@property(weak, nonatomic) IBOutlet UIView *shareContainer;
@property(weak, nonatomic) IBOutlet UIView *tipsTipContainer;
@property(weak, nonatomic) IBOutlet LLGitTip *gitTipButton;
@property(weak, nonatomic) IBOutlet UIWebView *newsView;
@property(weak, nonatomic) IBOutlet UIView *emergencyGeneratorContainer;
@property(weak, nonatomic) IBOutlet UITextField *emergencyName;
@property(weak, nonatomic) IBOutlet UITextField *emergencyMasterPassword;
@property(weak, nonatomic) IBOutlet UITextField *emergencySite;
@property(weak, nonatomic) IBOutlet UIStepper *emergencyCounterStepper;
@property(weak, nonatomic) IBOutlet UISegmentedControl *emergencyTypeControl;
@property(weak, nonatomic) IBOutlet UILabel *emergencyCounter;
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *emergencyActivity;
@property(weak, nonatomic) IBOutlet UIButton *emergencyPassword;
@property(weak, nonatomic) IBOutlet UIView *emergencyContentTipContainer;
- (IBAction)targetedUserAction:(UILongPressGestureRecognizer *)sender;
- (IBAction)facebook:(id)sender;
- (IBAction)twitter:(id)sender;
- (IBAction)google:(id)sender;
- (IBAction)mail:(id)sender;
- (IBAction)add:(id)sender;
- (IBAction)emergencyOpen:(id)sender;
- (IBAction)emergencyClose:(id)sender;
- (IBAction)emergencyCopy:(id)sender;
@end

File diff suppressed because it is too large Load Diff

View File

@ -805,7 +805,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
case MPActiveUserStateUserName:
case MPActiveUserStateMasterPasswordChoice:
case MPActiveUserStateMasterPasswordConfirmation: {
self.navigationBarToTopConstraint.priority = UILayoutPriorityDefaultHigh;
[self.navigationBarToTopConstraint layoutWithPriority:UILayoutPriorityDefaultHigh];
self.avatarCollectionView.scrollEnabled = NO;
self.entryContainer.alpha = 1;
self.footerContainer.alpha = 1;
@ -813,14 +813,13 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
break;
}
case MPActiveUserStateMinimized: {
self.navigationBarToTopConstraint.priority = 1;
[self.navigationBarToTopConstraint layoutWithPriority:1];
self.avatarCollectionView.scrollEnabled = NO;
self.entryContainer.alpha = 0;
self.footerContainer.alpha = 0;
break;
}
}
[self.navigationBarToTopConstraint apply];
} completion:^(BOOL finished) {
dbg(@"resume updates");
[_afterUpdates setSuspended:NO];

View File

@ -34,6 +34,8 @@
if (!self.initialURL)
self.initialURL = [NSURL URLWithString:@"http://masterpasswordapp.com"];
self.webNavigationItem.title = self.initialURL.host;
self.webView.alpha = 0;
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.initialURL]];
}

View File

@ -364,27 +364,34 @@
- (void)showExportForVC:(UIViewController *)viewController {
[PearlAlert showNotice:
@"This will export all your site names.\n\n"
@"You can open the export with a text editor to get an overview of all your sites.\n\n"
@"The file also acts as a personal backup of your site list in case you don't sync with iCloud/iTunes."
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
[PearlAlert showAlertWithTitle:@"Reveal Passwords?" message:
@"Would you like to make all your passwords visible in the export?\n\n"
@"A safe export will only include your stored passwords, in an encrypted manner, "
@"making the result safe from falling in the wrong hands.\n\n"
@"If all your passwords are shown and somebody else finds the export, "
@"they could gain access to all your sites!"
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 0)
// Safe Export
[self showExportRevealPasswords:NO forVC:viewController];
if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1)
// Show Passwords
[self showExportRevealPasswords:YES forVC:viewController];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Safe Export", @"Show Passwords", nil];
} otherTitles:nil];
[PearlAlert showAlertWithTitle:@"Exporting Your Sites"
message:@"An export is great for keeping a "
@"backup list of your accounts.\n\n"
@"When the file is ready, you will be "
@"able to mail it to yourself.\n"
@"You can open it with a text editor or "
@"with Master Password if you need to "
@"restore your list of sites."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
if (buttonIndex != [alert cancelButtonIndex])
[PearlAlert showAlertWithTitle:@"Show Passwords?"
message:@"Would you like to make all your passwords "
@"visible in the export file?\n\n"
@"A safe export will include all sites "
@"but make their passwords invisible.\n"
@"It is great as a backup and remains "
@"safe when fallen in the wrong hands."
viewStyle:UIAlertViewStyleDefault initAlert:nil
tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 0)
// Safe Export
[self showExportRevealPasswords:NO forVC:viewController];
if (buttonIndex_ == [alert_ firstOtherButtonIndex] + 1)
// Show Passwords
[self showExportRevealPasswords:YES forVC:viewController];
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Safe Export", @"Show Passwords", nil];
} cancelTitle:@"Cancel" otherTitles:@"Export Sites", nil];
}
- (void)showExportRevealPasswords:(BOOL)revealPasswords forVC:(UIViewController *)viewController {

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,6 @@
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 */; };
93D39B21156EC9A0B4C2BC83 /* MPPreferencesViewControllerOld.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390381D3D3AE241B5D341 /* MPPreferencesViewControllerOld.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 */; };
@ -50,6 +49,7 @@
93D39C8AD8EAB747856B3A8C /* LLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3923B42DA2DA18F287092 /* LLModel.m */; };
93D39CB5E2EC1078E898F46A /* MPPasswordLargeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3937863061C3916AF7AD2 /* MPPasswordLargeCell.m */; };
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; };
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39E34FD28D24FE3442C48 /* UITextView+PearlAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3977321EB249981821AB0 /* UITextView+PearlAttributes.m */; };
93D39EAA4D064193074D3021 /* MPFixable.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A813CA9D7E192261ED2 /* MPFixable.m */; };
@ -249,19 +249,10 @@
DABD3C091711E2DC00CF925C /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BBA1711E2DC00CF925C /* MPUserEntity.m */; };
DABD3C141711E2DC00CF925C /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD01711E2DC00CF925C /* MasterPassword.xcdatamodeld */; };
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */; };
DABD3C161711E2DC00CF925C /* MPAppViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BDB1711E2DC00CF925C /* MPAppViewController.m */; };
DABD3C171711E2DC00CF925C /* MPAppsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BDD1711E2DC00CF925C /* MPAppsViewController.m */; };
DABD3C181711E2DC00CF925C /* MPElementListAllViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BDF1711E2DC00CF925C /* MPElementListAllViewController.m */; };
DABD3C191711E2DC00CF925C /* MPElementListCellView.xib in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BE01711E2DC00CF925C /* MPElementListCellView.xib */; };
DABD3C1A1711E2DC00CF925C /* MPElementListController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BE21711E2DC00CF925C /* MPElementListController.m */; };
DABD3C1B1711E2DC00CF925C /* MPElementListSearchController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BE51711E2DC00CF925C /* MPElementListSearchController.m */; };
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */; };
DABD3C1D1711E2DC00CF925C /* MPMainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BE91711E2DC00CF925C /* MPMainViewController.m */; };
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */; };
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */; };
DABD3C201711E2DC00CF925C /* MPUnlockViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BEF1711E2DC00CF925C /* MPUnlockViewController.m */; };
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */; };
DABD3C221711E2DC00CF925C /* MainStoryboard_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BF21711E2DC00CF925C /* MainStoryboard_iPhone.storyboard */; };
DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */; };
DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BF91711E2DC00CF925C /* Settings.bundle */; };
DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */; };
@ -511,7 +502,6 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
93D390381D3D3AE241B5D341 /* MPPreferencesViewControllerOld.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPreferencesViewControllerOld.m; sourceTree = "<group>"; };
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>"; };
93D39097C0AAE62C1C321BFC /* MPPasswordTypesCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordTypesCell.m; sourceTree = "<group>"; };
@ -540,6 +530,7 @@
93D395BA6B2CFF5F49A4D25F /* MPPasswordLargeStoredCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeStoredCell.h; sourceTree = "<group>"; };
93D3966B527CA47A0A661CE2 /* MPPasswordLargeDeleteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordLargeDeleteCell.h; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3970502644794E8A027BE /* MPNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNavigationController.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>"; };
93D3977321EB249981821AB0 /* UITextView+PearlAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+PearlAttributes.m"; sourceTree = "<group>"; };
@ -551,7 +542,6 @@
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; };
93D3993422E207BF0B21D089 /* MPPasswordLargeGeneratedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordLargeGeneratedCell.m; sourceTree = "<group>"; };
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; };
93D39961CD6A43648CC0B0DB /* MPPreferencesViewControllerOld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPreferencesViewControllerOld.h; sourceTree = "<group>"; };
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; };
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; };
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = "<group>"; };
@ -574,6 +564,7 @@
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = "<group>"; };
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = "<group>"; };
93D39C8E26B06F01566785B7 /* LLToggleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLToggleViewController.m; sourceTree = "<group>"; };
93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = "<group>"; };
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.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>"; };
@ -1310,31 +1301,14 @@
DABD3BD41711E2DC00CF925C /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSAppDelegate.h; sourceTree = "<group>"; };
DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSAppDelegate.m; sourceTree = "<group>"; };
DABD3BDA1711E2DC00CF925C /* MPAppViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppViewController.h; sourceTree = "<group>"; };
DABD3BDB1711E2DC00CF925C /* MPAppViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppViewController.m; sourceTree = "<group>"; };
DABD3BDC1711E2DC00CF925C /* MPAppsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppsViewController.h; sourceTree = "<group>"; };
DABD3BDD1711E2DC00CF925C /* MPAppsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppsViewController.m; sourceTree = "<group>"; };
DABD3BDE1711E2DC00CF925C /* MPElementListAllViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementListAllViewController.h; sourceTree = "<group>"; };
DABD3BDF1711E2DC00CF925C /* MPElementListAllViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementListAllViewController.m; sourceTree = "<group>"; };
DABD3BE01711E2DC00CF925C /* MPElementListCellView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPElementListCellView.xib; sourceTree = "<group>"; };
DABD3BE11711E2DC00CF925C /* MPElementListController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementListController.h; sourceTree = "<group>"; };
DABD3BE21711E2DC00CF925C /* MPElementListController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementListController.m; sourceTree = "<group>"; };
DABD3BE31711E2DC00CF925C /* MPElementListDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementListDelegate.h; sourceTree = "<group>"; };
DABD3BE41711E2DC00CF925C /* MPElementListSearchController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementListSearchController.h; sourceTree = "<group>"; };
DABD3BE51711E2DC00CF925C /* MPElementListSearchController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementListSearchController.m; sourceTree = "<group>"; };
DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGuideViewController.h; sourceTree = "<group>"; };
DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGuideViewController.m; sourceTree = "<group>"; };
DABD3BE81711E2DC00CF925C /* MPMainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMainViewController.h; sourceTree = "<group>"; };
DABD3BE91711E2DC00CF925C /* MPMainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMainViewController.m; sourceTree = "<group>"; };
DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPreferencesViewController.h; sourceTree = "<group>"; };
DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPreferencesViewController.m; sourceTree = "<group>"; };
DABD3BEC1711E2DC00CF925C /* MPTypeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypeViewController.h; sourceTree = "<group>"; };
DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTypeViewController.m; sourceTree = "<group>"; };
DABD3BEE1711E2DC00CF925C /* MPUnlockViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUnlockViewController.h; sourceTree = "<group>"; };
DABD3BEF1711E2DC00CF925C /* MPUnlockViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUnlockViewController.m; sourceTree = "<group>"; };
DABD3BF01711E2DC00CF925C /* MPiOSConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSConfig.h; sourceTree = "<group>"; };
DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSConfig.m; sourceTree = "<group>"; };
DABD3BF21711E2DC00CF925C /* MainStoryboard_iPhone.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboard_iPhone.storyboard; sourceTree = "<group>"; };
DABD3BF31711E2DC00CF925C /* MasterPassword-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MasterPassword-Info.plist"; sourceTree = "<group>"; };
DABD3BF41711E2DC00CF925C /* MasterPassword-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MasterPassword-Prefix.pch"; sourceTree = "<group>"; };
DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = MasterPassword.entitlements; sourceTree = "<group>"; };
@ -2523,35 +2497,16 @@
93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */,
93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */,
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */,
93D390381D3D3AE241B5D341 /* MPPreferencesViewControllerOld.m */,
93D39961CD6A43648CC0B0DB /* MPPreferencesViewControllerOld.h */,
DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */,
DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */,
DABD3BDA1711E2DC00CF925C /* MPAppViewController.h */,
DABD3BDB1711E2DC00CF925C /* MPAppViewController.m */,
DABD3BDC1711E2DC00CF925C /* MPAppsViewController.h */,
DABD3BDD1711E2DC00CF925C /* MPAppsViewController.m */,
DABD3BDE1711E2DC00CF925C /* MPElementListAllViewController.h */,
DABD3BDF1711E2DC00CF925C /* MPElementListAllViewController.m */,
DABD3BE01711E2DC00CF925C /* MPElementListCellView.xib */,
DABD3BE11711E2DC00CF925C /* MPElementListController.h */,
DABD3BE21711E2DC00CF925C /* MPElementListController.m */,
DABD3BE31711E2DC00CF925C /* MPElementListDelegate.h */,
DABD3BE41711E2DC00CF925C /* MPElementListSearchController.h */,
DABD3BE51711E2DC00CF925C /* MPElementListSearchController.m */,
DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */,
DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */,
DABD3BE81711E2DC00CF925C /* MPMainViewController.h */,
DABD3BE91711E2DC00CF925C /* MPMainViewController.m */,
DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */,
DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */,
DABD3BEC1711E2DC00CF925C /* MPTypeViewController.h */,
DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */,
DABD3BEE1711E2DC00CF925C /* MPUnlockViewController.h */,
DABD3BEF1711E2DC00CF925C /* MPUnlockViewController.m */,
DABD3BF01711E2DC00CF925C /* MPiOSConfig.h */,
DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */,
DABD3BF21711E2DC00CF925C /* MainStoryboard_iPhone.storyboard */,
DABD3BF31711E2DC00CF925C /* MasterPassword-Info.plist */,
DABD3BF41711E2DC00CF925C /* MasterPassword-Prefix.pch */,
DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */,
@ -2595,6 +2550,8 @@
93D390FD93EFCFECB5193DEF /* MPPasswordsCoachmarkViewController.h */,
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */,
93D39F556F2F142740A65E59 /* MPWebViewController.h */,
93D39CC01630D0421205C4C4 /* MPNavigationController.m */,
93D3970502644794E8A027BE /* MPNavigationController.h */,
);
path = iOS;
sourceTree = "<group>";
@ -3676,9 +3633,7 @@
DADEF4121810D2940052CA3E /* love-lyndir.button.green.png in Resources */,
DABD3B9D1711E29800CF925C /* social-twitter.png in Resources */,
DABD3B9E1711E29800CF925C /* social-twitter@2x.png in Resources */,
DABD3C191711E2DC00CF925C /* MPElementListCellView.xib in Resources */,
DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */,
DABD3C221711E2DC00CF925C /* MainStoryboard_iPhone.storyboard in Resources */,
DABD3C241711E2DC00CF925C /* MasterPassword.entitlements in Resources */,
DABD3C251711E2DC00CF925C /* Settings.bundle in Resources */,
DABD3C261711E2DC00CF925C /* InfoPlist.strings in Resources */,
@ -3777,16 +3732,9 @@
DABD3C091711E2DC00CF925C /* MPUserEntity.m in Sources */,
DABD3C141711E2DC00CF925C /* MasterPassword.xcdatamodeld in Sources */,
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */,
DABD3C161711E2DC00CF925C /* MPAppViewController.m in Sources */,
DABD3C171711E2DC00CF925C /* MPAppsViewController.m in Sources */,
DABD3C181711E2DC00CF925C /* MPElementListAllViewController.m in Sources */,
DABD3C1A1711E2DC00CF925C /* MPElementListController.m in Sources */,
DABD3C1B1711E2DC00CF925C /* MPElementListSearchController.m in Sources */,
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */,
DABD3C1D1711E2DC00CF925C /* MPMainViewController.m in Sources */,
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */,
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */,
DABD3C201711E2DC00CF925C /* MPUnlockViewController.m in Sources */,
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */,
DABD3C271711E2DC00CF925C /* main.m in Sources */,
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */,
@ -3805,13 +3753,13 @@
93D391ED37C9F687FA51EAA1 /* MPEmergencySegue.m in Sources */,
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */,
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */,
93D39B21156EC9A0B4C2BC83 /* MPPreferencesViewControllerOld.m in Sources */,
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */,
93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */,
93D391C07818F4C2DC1B6956 /* MPPasswordsCoachmarkViewController.m in Sources */,
93D39EAA4D064193074D3021 /* MPFixable.m in Sources */,
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */,
93D398BD8B83FEE8BE4EFFFC /* MPPasswordLargeDeleteCell.m in Sources */,
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -251,7 +251,7 @@
<segue destination="Sd5-eW-Cx2" kind="modal" identifier="web" id="gtb-zE-u9H"/>
</connections>
</button>
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="069-Pu-yXe" userLabel="GitTip Tip">
<view userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="069-Pu-yXe" userLabel="Thanks Tip">
<rect key="frame" x="42" y="0.0" width="236" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
@ -410,7 +410,7 @@
<!--Navigation Controller-->
<scene sceneID="bzn-yi-kMJ">
<objects>
<navigationController definesPresentationContext="YES" navigationBarHidden="YES" id="Q1S-vU-GGO" sceneMemberID="viewController">
<navigationController definesPresentationContext="YES" navigationBarHidden="YES" id="Q1S-vU-GGO" customClass="MPNavigationController" sceneMemberID="viewController">
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="4yl-zs-iUd">
<autoresizingMask key="autoresizingMask"/>
@ -504,7 +504,7 @@
<sections>
<tableViewSection id="FEv-Rb-jst">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="97" id="R30-AU-bR6" userLabel="Sign Out">
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="97" id="R30-AU-bR6" userLabel="Sign Out">
<rect key="frame" x="0.0" y="0.0" width="320" height="97"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="R30-AU-bR6" id="f6h-Ff-2Qc">
@ -538,7 +538,7 @@
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="216" id="B8R-iE-Ffe" userLabel="Default Password Type">
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="216" id="B8R-iE-Ffe" userLabel="Default Password Type">
<rect key="frame" x="0.0" y="97" width="320" height="216"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="B8R-iE-Ffe" id="8r5-Zc-TRj">
@ -604,7 +604,7 @@
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="220" id="Sz1-JP-dw2" userLabel="Avatar">
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="220" id="Sz1-JP-dw2" userLabel="Avatar">
<rect key="frame" x="0.0" y="313" width="320" height="220"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Sz1-JP-dw2" id="R4X-LE-ir9">
@ -684,7 +684,7 @@
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="230" id="fRZ-Uh-FR8" userLabel="Save Password">
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="230" id="fRZ-Uh-FR8" userLabel="Save Password">
<rect key="frame" x="0.0" y="533" width="320" height="230"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fRZ-Uh-FR8" id="qCQ-L5-teL">
@ -728,7 +728,7 @@
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="97" id="9QG-lM-ymM" userLabel="Feedback">
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="97" id="9QG-lM-ymM" userLabel="Feedback">
<rect key="frame" x="0.0" y="763" width="320" height="97"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="9QG-lM-ymM" id="hK8-XQ-lLz">
@ -762,7 +762,7 @@
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="97" id="eth-Dc-JYn" userLabel="Reveal Coachmarks">
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="97" id="eth-Dc-JYn" userLabel="Reveal Coachmarks">
<rect key="frame" x="0.0" y="860" width="320" height="97"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="eth-Dc-JYn" id="8m6-pP-lda">
@ -796,7 +796,7 @@
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="110" id="UdB-BV-AHA" userLabel="Check Inconsistencies">
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="110" id="UdB-BV-AHA" userLabel="Check Inconsistencies">
<rect key="frame" x="0.0" y="957" width="320" height="110"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="UdB-BV-AHA" id="V2Y-nu-jhZ">
@ -830,7 +830,7 @@
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="125" id="IVT-Rs-nTu" userLabel="Export">
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="125" id="IVT-Rs-nTu" userLabel="Export">
<rect key="frame" x="0.0" y="1067" width="320" height="125"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="IVT-Rs-nTu" id="Q5J-2f-mmz">
@ -865,6 +865,86 @@
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" shouldIndentWhileEditing="NO" rowHeight="153" id="hmf-Wz-9l2" userLabel="Footer">
<rect key="frame" x="0.0" y="1192" width="320" height="153"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hmf-Wz-9l2" id="AL3-2q-tgO">
<rect key="frame" x="0.0" y="0.0" width="320" height="152"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" text="© 2012-2014, Maarten Billemont (lhunath)" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sPw-mV-mFF">
<rect key="frame" x="20" y="4" width="280" height="12"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Exo2.0-Thin" family="Exo 2.0" pointSize="10"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Rl7-cr-FHf">
<rect key="frame" x="20" y="24" width="280" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
<state key="normal" title="Home Page">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="homePageButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="ptD-cv-NMr"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="epW-Rm-9St">
<rect key="frame" x="20" y="58" width="280" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
<state key="normal" title="Understanding Master Password's Security">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="securityButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Efv-cp-Xfh"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LTN-ch-h8D">
<rect key="frame" x="20" y="92" width="280" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
<state key="normal" title="Get the Master Password source code">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="sourceButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="Y3O-di-CZo"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Z60-lc-Nka">
<rect key="frame" x="20" y="126" width="280" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Exo2.0-Regular" family="Exo 2.0" pointSize="11"/>
<state key="normal" title="Send Thanks">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="thanksButton:" destination="JFc-sj-awD" eventType="touchUpInside" id="SqG-hx-mzF"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="LTN-ch-h8D" secondAttribute="trailing" constant="20" symbolic="YES" id="1W6-6Q-Bdv"/>
<constraint firstItem="LTN-ch-h8D" firstAttribute="top" secondItem="epW-Rm-9St" secondAttribute="bottom" constant="8" symbolic="YES" id="7YP-IL-yTY"/>
<constraint firstItem="Z60-lc-Nka" firstAttribute="leading" secondItem="AL3-2q-tgO" secondAttribute="leading" constant="20" symbolic="YES" id="Fnk-lP-q0U"/>
<constraint firstAttribute="bottom" secondItem="Z60-lc-Nka" secondAttribute="bottom" id="Jbk-vs-bo1"/>
<constraint firstItem="sPw-mV-mFF" firstAttribute="leading" secondItem="AL3-2q-tgO" secondAttribute="leading" constant="20" symbolic="YES" id="Jme-5a-dNH"/>
<constraint firstItem="LTN-ch-h8D" firstAttribute="leading" secondItem="AL3-2q-tgO" secondAttribute="leading" constant="20" symbolic="YES" id="RpC-WA-bnf"/>
<constraint firstItem="sPw-mV-mFF" firstAttribute="top" secondItem="AL3-2q-tgO" secondAttribute="top" constant="4" id="X4M-st-0KA"/>
<constraint firstItem="Z60-lc-Nka" firstAttribute="top" secondItem="LTN-ch-h8D" secondAttribute="bottom" constant="8" symbolic="YES" id="YSW-fw-h4i"/>
<constraint firstItem="epW-Rm-9St" firstAttribute="top" secondItem="Rl7-cr-FHf" secondAttribute="bottom" constant="8" symbolic="YES" id="b0H-uh-d3G"/>
<constraint firstItem="Rl7-cr-FHf" firstAttribute="top" secondItem="sPw-mV-mFF" secondAttribute="bottom" constant="8" symbolic="YES" id="gwp-Uq-BEm"/>
<constraint firstAttribute="trailing" secondItem="Z60-lc-Nka" secondAttribute="trailing" constant="20" symbolic="YES" id="iCv-UX-pPf"/>
<constraint firstAttribute="trailing" secondItem="Rl7-cr-FHf" secondAttribute="trailing" constant="20" symbolic="YES" id="oej-WB-7Vb"/>
<constraint firstItem="Rl7-cr-FHf" firstAttribute="leading" secondItem="AL3-2q-tgO" secondAttribute="leading" constant="20" symbolic="YES" id="r2V-pM-d9B"/>
<constraint firstItem="epW-Rm-9St" firstAttribute="leading" secondItem="AL3-2q-tgO" secondAttribute="leading" constant="20" symbolic="YES" id="rve-aQ-Ggt"/>
<constraint firstAttribute="trailing" secondItem="epW-Rm-9St" secondAttribute="trailing" constant="20" symbolic="YES" id="sHt-wW-wHU"/>
<constraint firstAttribute="trailing" secondItem="sPw-mV-mFF" secondAttribute="trailing" constant="20" symbolic="YES" id="xjb-mS-yVZ"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
@ -1130,6 +1210,13 @@
<rect key="frame" x="0.0" y="0.0" width="300" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" tag="3" contentMode="left" text="> age of the universe" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="GrV-gX-eCo" userLabel="Strength">
<rect key="frame" x="0.0" y="0.0" width="300" height="12"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="10"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" alpha="0.029999999999999999" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Long Password" lineBreakMode="clip" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qAD-3v-3aq" userLabel="Type">
<rect key="frame" x="-10" y="19" width="604" height="101"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
@ -1251,10 +1338,13 @@
<constraint firstAttribute="trailing" secondItem="q75-Uz-86O" secondAttribute="trailing" constant="8" id="ZsY-wg-aJt"/>
<constraint firstItem="q75-Uz-86O" firstAttribute="leading" secondItem="302-fI-maQ" secondAttribute="leading" constant="8" id="cee-Hz-2IS"/>
<constraint firstAttribute="trailing" secondItem="cOv-2G-EAP" secondAttribute="trailing" id="fNx-v1-XM3"/>
<constraint firstItem="GrV-gX-eCo" firstAttribute="leading" secondItem="302-fI-maQ" secondAttribute="leading" id="hX4-Yf-rzp"/>
<constraint firstItem="cOv-2G-EAP" firstAttribute="leading" secondItem="fQc-Fn-JDq" secondAttribute="trailing" constant="-9" id="hqr-ru-rB7"/>
<constraint firstItem="GrV-gX-eCo" firstAttribute="top" secondItem="302-fI-maQ" secondAttribute="top" id="ptG-Jr-1R8"/>
<constraint firstItem="cOv-2G-EAP" firstAttribute="centerY" secondItem="fQc-Fn-JDq" secondAttribute="centerY" id="rrx-LF-Hk9"/>
<constraint firstItem="fQc-Fn-JDq" firstAttribute="centerY" secondItem="I2J-B6-5rE" secondAttribute="centerY" id="uR7-lg-A9q"/>
<constraint firstItem="qek-2l-YQf" firstAttribute="leading" secondItem="302-fI-maQ" secondAttribute="leading" constant="8" id="wUJ-7N-Z1z"/>
<constraint firstAttribute="trailing" secondItem="GrV-gX-eCo" secondAttribute="trailing" id="yDV-ZB-a8l"/>
</constraints>
<connections>
<outlet property="contentField" destination="q75-Uz-86O" id="nbM-vd-uZi"/>
@ -1262,6 +1352,7 @@
<outlet property="counterLabel" destination="I2J-B6-5rE" id="C4b-gE-XHW"/>
<outlet property="loginButton" destination="cOv-2G-EAP" id="WoR-eP-Ztq"/>
<outlet property="nameLabel" destination="qek-2l-YQf" id="CcC-PM-kMx"/>
<outlet property="strengthLabel" destination="GrV-gX-eCo" id="e6J-5c-Dln"/>
<outlet property="typeLabel" destination="qAD-3v-3aq" id="rAM-Kf-5xO"/>
<outlet property="upgradeButton" destination="iLD-rv-uZZ" id="OKi-9X-R6F"/>
</connections>