2011-11-30 21:42:40 +00:00
|
|
|
//
|
|
|
|
// OPMainViewController.m
|
|
|
|
// OnePassword
|
|
|
|
//
|
|
|
|
// Created by Maarten Billemont on 24/11/11.
|
|
|
|
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "OPMainViewController.h"
|
2012-01-05 00:44:15 +00:00
|
|
|
#import "OPAppDelegate.h"
|
|
|
|
#import "OPContentViewController.h"
|
2012-01-19 16:40:39 +00:00
|
|
|
#import "OPElementGeneratedEntity.h"
|
|
|
|
#import "OPElementStoredEntity.h"
|
2012-01-05 00:44:15 +00:00
|
|
|
|
|
|
|
#import <MobileCoreServices/MobileCoreServices.h>
|
|
|
|
|
|
|
|
|
|
|
|
@interface OPMainViewController (Private)
|
|
|
|
|
|
|
|
- (void)updateAnimated:(BOOL)animated;
|
|
|
|
- (void)updateWasAnimated:(BOOL)animated;
|
2012-01-24 23:30:43 +00:00
|
|
|
- (void)showContentTip:(NSString *)message withIcon:(UIImageView *)icon;
|
|
|
|
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message;
|
|
|
|
- (void)updateElement:(void (^)(void))updateElement;
|
2012-01-05 00:44:15 +00:00
|
|
|
|
|
|
|
@end
|
2011-11-30 21:42:40 +00:00
|
|
|
|
|
|
|
@implementation OPMainViewController
|
2012-01-05 00:44:15 +00:00
|
|
|
@synthesize activeElement = _activeElement;
|
|
|
|
@synthesize searchResultsController = _searchResultsController;
|
2012-01-16 08:51:08 +00:00
|
|
|
@synthesize typeButton = _typeButton;
|
|
|
|
@synthesize helpView = _helpView;
|
|
|
|
@synthesize siteName = _siteName;
|
|
|
|
@synthesize passwordCounter = _passwordCounter;
|
|
|
|
@synthesize passwordIncrementer = _passwordIncrementer;
|
2012-01-19 16:40:39 +00:00
|
|
|
@synthesize passwordEdit = _passwordEdit;
|
|
|
|
@synthesize contentContainer = _contentContainer;
|
|
|
|
@synthesize helpContainer = _helpContainer;
|
2012-01-24 23:30:43 +00:00
|
|
|
@synthesize contentTipContainer = _copiedContainer;
|
|
|
|
@synthesize alertContainer = _alertContainer;
|
|
|
|
@synthesize alertTitle = _alertTitle;
|
|
|
|
@synthesize alertBody = _alertBody;
|
|
|
|
@synthesize contentTipBody = _contentTipBody;
|
|
|
|
@synthesize contentTipEditIcon = _contentTipEditIcon;
|
|
|
|
@synthesize searchTipContainer = _searchTip;
|
2012-01-05 00:44:15 +00:00
|
|
|
@synthesize contentField = _contentField;
|
2011-11-30 21:42:40 +00:00
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
#pragma mark - View lifecycle
|
2011-11-30 21:42:40 +00:00
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
|
|
|
|
|
|
|
|
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad || interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
|
2011-11-30 21:42:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
|
|
|
|
|
|
if ([[segue identifier] isEqualToString:@"OP_Main_ChooseType"])
|
|
|
|
[[segue destinationViewController] setDelegate:self];
|
|
|
|
if ([[segue identifier] isEqualToString:@"OP_Main_Content"])
|
|
|
|
((OPContentViewController *)[segue destinationViewController]).activeElement = self.activeElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)viewWillAppear:(BOOL)animated {
|
|
|
|
|
|
|
|
[super viewWillAppear:animated];
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
if (![self.searchTipContainer.superview isKindOfClass:[UIWindow class]]) {
|
|
|
|
// Put the search tip on the window so it's above the nav bar.
|
|
|
|
[self.searchTipContainer removeFromSuperview];
|
|
|
|
[[UIApplication sharedApplication].keyWindow addSubview:self.searchTipContainer];
|
|
|
|
self.searchTipContainer.frame = CGRectSetY(self.searchTipContainer.frame, self.searchTipContainer.frame.origin.y
|
|
|
|
+ self.navigationController.navigationBar.frame.size.height /* Nav */ + 20 /* Status */);
|
|
|
|
}
|
|
|
|
if (!self.activeElement.name)
|
|
|
|
[UIView animateWithDuration:animated? 0.2f: 0 animations:^{
|
|
|
|
self.searchTipContainer.alpha = 1;
|
|
|
|
}];
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
[self toggleHelp:[[OPConfig get].helpHidden boolValue]];
|
2012-01-05 00:44:15 +00:00
|
|
|
[self updateAnimated:NO];
|
|
|
|
}
|
2011-11-30 21:42:40 +00:00
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (void)viewDidLoad {
|
|
|
|
|
|
|
|
// Because IB's edit button doesn't auto-toggle self.editable like editButtonItem does.
|
2012-01-24 23:30:43 +00:00
|
|
|
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"ui_background"]];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:[NSOperationQueue mainQueue]
|
|
|
|
usingBlock:^(NSNotification *note) {
|
|
|
|
if (![OPAppDelegate get].keyPhrase) {
|
|
|
|
self.activeElement = nil;
|
|
|
|
[self updateAnimated:NO];
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:[NSOperationQueue mainQueue]
|
|
|
|
usingBlock:^(NSNotification *note) {
|
|
|
|
if (![OPAppDelegate get].keyPhrase) {
|
|
|
|
self.activeElement = nil;
|
|
|
|
[self updateAnimated:NO];
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
|
|
|
|
[self closeAlert];
|
2012-01-19 16:40:39 +00:00
|
|
|
|
2011-11-30 21:42:40 +00:00
|
|
|
[super viewDidLoad];
|
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (void)viewDidUnload {
|
|
|
|
|
|
|
|
[self setContentField:nil];
|
2012-01-16 08:51:08 +00:00
|
|
|
[self setTypeButton:nil];
|
2012-01-05 00:44:15 +00:00
|
|
|
[self setSearchResultsController:nil];
|
2012-01-16 08:51:08 +00:00
|
|
|
[self setHelpView:nil];
|
|
|
|
[self setSiteName:nil];
|
|
|
|
[self setPasswordCounter:nil];
|
|
|
|
[self setPasswordIncrementer:nil];
|
2012-01-19 16:40:39 +00:00
|
|
|
[self setPasswordEdit:nil];
|
|
|
|
[self setContentContainer:nil];
|
|
|
|
[self setHelpContainer:nil];
|
2012-01-24 23:30:43 +00:00
|
|
|
[self setContentTipContainer:nil];
|
|
|
|
[self setAlertContainer:nil];
|
|
|
|
[self setAlertTitle:nil];
|
|
|
|
[self setAlertBody:nil];
|
|
|
|
[self setContentTipBody:nil];
|
|
|
|
[self setContentTipEditIcon:nil];
|
|
|
|
[self setSearchTipContainer:nil];
|
2011-11-30 21:42:40 +00:00
|
|
|
[super viewDidUnload];
|
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
|
|
|
|
|
|
|
|
[super setEditing:editing animated:animated];
|
|
|
|
|
|
|
|
[self updateAnimated:animated];
|
2011-11-30 21:42:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (void)updateAnimated:(BOOL)animated {
|
2012-01-19 16:40:39 +00:00
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
[[OPAppDelegate get] saveContext];
|
|
|
|
|
|
|
|
if (animated)
|
|
|
|
[UIView animateWithDuration:0.2 animations:^{
|
|
|
|
[self updateWasAnimated:YES];
|
|
|
|
}];
|
|
|
|
else
|
|
|
|
[self updateWasAnimated:NO];
|
2011-11-30 21:42:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (void)updateWasAnimated:(BOOL)animated {
|
2012-01-19 16:40:39 +00:00
|
|
|
|
2012-01-16 08:51:08 +00:00
|
|
|
NSUInteger chapter = self.activeElement? 2: 1;
|
|
|
|
[self.helpView loadRequest:
|
2012-01-19 16:40:39 +00:00
|
|
|
[NSURLRequest requestWithURL:
|
|
|
|
[NSURL URLWithString:[NSString stringWithFormat:@"#%d", chapter] relativeToURL:
|
|
|
|
[[NSBundle mainBundle] URLForResource:@"help" withExtension:@"html"]]]];
|
2012-01-24 23:30:43 +00:00
|
|
|
[self.helpView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"setClass('%@');",
|
|
|
|
ClassNameFromOPElementType(self.activeElement.type)]];
|
2012-01-19 16:40:39 +00:00
|
|
|
|
2012-01-16 08:51:08 +00:00
|
|
|
self.siteName.text = self.activeElement.name;
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
self.passwordCounter.alpha = self.activeElement.type & OPElementTypeClassCalculated? 0.5f: 0;
|
|
|
|
self.passwordIncrementer.alpha = self.activeElement.type & OPElementTypeClassCalculated? 0.5f: 0;
|
|
|
|
self.passwordEdit.alpha = self.activeElement.type & OPElementTypeClassStored? 0.5f: 0;
|
2012-01-19 16:40:39 +00:00
|
|
|
|
2012-01-16 08:51:08 +00:00
|
|
|
[self.typeButton setTitle:NSStringFromOPElementType(self.activeElement.type)
|
|
|
|
forState:UIControlStateNormal];
|
2012-01-19 16:40:39 +00:00
|
|
|
self.typeButton.alpha = NSStringFromOPElementType(self.activeElement.type).length? 1: 0;
|
|
|
|
|
|
|
|
self.contentField.enabled = NO;
|
|
|
|
|
2012-01-19 21:57:35 +00:00
|
|
|
if ([self.activeElement isKindOfClass:[OPElementGeneratedEntity class]])
|
|
|
|
self.passwordCounter.text = [NSString stringWithFormat:@"%d", ((OPElementGeneratedEntity *) self.activeElement).counter];
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
|
|
|
NSString *contentDescription = self.activeElement.contentDescription;
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
self.contentField.text = contentDescription;
|
|
|
|
});
|
|
|
|
});
|
2011-11-30 21:42:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
- (void)showContentTip:(NSString *)message withIcon:(UIImageView *)icon {
|
|
|
|
|
|
|
|
self.contentTipBody.text = message;
|
|
|
|
|
|
|
|
icon.hidden = NO;
|
|
|
|
[UIView animateWithDuration:0.2f animations:^{
|
|
|
|
self.contentTipContainer.alpha = 1;
|
|
|
|
} completion:^(BOOL finished) {
|
|
|
|
if (!finished) {
|
|
|
|
icon.hidden = YES;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 5.0f * NSEC_PER_SEC);
|
|
|
|
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
|
|
|
|
[UIView animateWithDuration:0.2f animations:^{
|
|
|
|
self.contentTipContainer.alpha = 0;
|
|
|
|
} completion:^(BOOL finished) {
|
|
|
|
icon.hidden = YES;
|
|
|
|
}];
|
|
|
|
});
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
|
|
|
|
|
|
|
|
self.alertTitle.text = title;
|
|
|
|
if ([self.alertBody.text length])
|
|
|
|
self.alertBody.text = [NSString stringWithFormat:@"%@\n\n---\n\n%@", self.alertBody.text, message];
|
|
|
|
else
|
|
|
|
self.alertBody.text = message;
|
|
|
|
|
|
|
|
[UIView animateWithDuration:0.2f animations:^{
|
|
|
|
self.alertContainer.alpha = 1;
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
#pragma mark - Protocols
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
- (IBAction)copyContent {
|
2012-01-05 00:44:15 +00:00
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
if (!self.activeElement)
|
|
|
|
return;
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
[[UIPasteboard generalPasteboard] setValue:self.activeElement.content
|
|
|
|
forPasteboardType:self.activeElement.contentUTI];
|
2012-01-24 23:30:43 +00:00
|
|
|
|
|
|
|
[self showContentTip:@"Copied!" withIcon:nil];
|
2011-11-30 21:42:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
- (IBAction)incrementPasswordCounter {
|
2012-01-12 16:28:20 +00:00
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
if (![self.activeElement isKindOfClass:[OPElementGeneratedEntity class]])
|
|
|
|
// Not of a type that supports a password counter;
|
|
|
|
return;
|
|
|
|
|
|
|
|
[self updateElement:^{
|
|
|
|
++((OPElementGeneratedEntity *) self.activeElement).counter;
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)updateElement:(void (^)(void))updateElement {
|
|
|
|
|
|
|
|
// Update password counter.
|
|
|
|
NSString *oldPassword = self.activeElement.contentDescription;
|
|
|
|
updateElement();
|
|
|
|
NSString *newPassword = self.activeElement.contentDescription;
|
|
|
|
[self updateAnimated:YES];
|
|
|
|
|
|
|
|
// Show new and old password.
|
|
|
|
if (oldPassword && ![oldPassword isEqualToString:newPassword])
|
|
|
|
[self showAlertWithTitle:@"Password Changed!" message:l(@"The password for %@ has changed.\n\n"
|
|
|
|
@"Don't forget to update the site with your new password! "
|
|
|
|
@"Your old password was:\n"
|
|
|
|
@"%@", self.activeElement.name, oldPassword)];
|
2012-01-12 16:28:20 +00:00
|
|
|
}
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
- (IBAction)editPassword {
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
if (self.activeElement.type & OPElementTypeClassStored) {
|
2012-01-19 16:40:39 +00:00
|
|
|
self.contentField.enabled = YES;
|
|
|
|
[self.contentField becomeFirstResponder];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (IBAction)toggleHelp {
|
|
|
|
|
|
|
|
[UIView animateWithDuration:0.3f animations:^{
|
|
|
|
if (self.helpContainer.frame.origin.y < 400)
|
|
|
|
[self toggleHelp:YES];
|
|
|
|
else
|
|
|
|
[self toggleHelp:NO];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)toggleHelp:(BOOL)hidden {
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
if (hidden) {
|
|
|
|
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 373);
|
|
|
|
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 415);
|
|
|
|
[OPConfig get].helpHidden = [NSNumber numberWithBool:YES];
|
|
|
|
} else {
|
|
|
|
self.contentContainer.frame = CGRectSetHeight(self.contentContainer.frame, 175);
|
|
|
|
self.helpContainer.frame = CGRectSetY(self.helpContainer.frame, 216);
|
|
|
|
[OPConfig get].helpHidden = [NSNumber numberWithBool:NO];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (IBAction)closeAlert {
|
|
|
|
|
|
|
|
[UIView animateWithDuration:0.3f animations:^{
|
|
|
|
self.alertContainer.alpha = 0;
|
|
|
|
} completion:^(BOOL finished) {
|
|
|
|
self.alertBody.text = nil;
|
|
|
|
}];
|
2012-01-16 08:51:08 +00:00
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (void)didSelectType:(OPElementType)type {
|
|
|
|
|
2012-01-24 23:30:43 +00:00
|
|
|
[self updateElement:^{
|
|
|
|
// Update password type.
|
|
|
|
if (ClassFromOPElementType(type) != ClassFromOPElementType(self.activeElement.type)) {
|
|
|
|
// Type requires a different class of element. Recreate the element.
|
|
|
|
OPElementEntity *newElement = [NSEntityDescription insertNewObjectForEntityForName:ClassNameFromOPElementType(type)
|
|
|
|
inManagedObjectContext:[OPAppDelegate managedObjectContext]];
|
|
|
|
newElement.name = self.activeElement.name;
|
|
|
|
newElement.uses = self.activeElement.uses;
|
|
|
|
newElement.lastUsed = self.activeElement.lastUsed;
|
|
|
|
newElement.contentUTI = self.activeElement.contentUTI;
|
|
|
|
newElement.contentType = self.activeElement.contentType;
|
|
|
|
|
|
|
|
[[OPAppDelegate managedObjectContext] deleteObject:self.activeElement];
|
|
|
|
self.activeElement = newElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.activeElement.type = type;
|
|
|
|
|
|
|
|
if (type & OPElementTypeClassStored && !self.activeElement.contentDescription)
|
|
|
|
[self showContentTip:@"Tap to set a password." withIcon:self.contentTipEditIcon];
|
|
|
|
}];
|
2011-11-30 21:42:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
- (void)didSelectElement:(OPElementEntity *)element {
|
|
|
|
|
|
|
|
self.activeElement = element;
|
2012-01-12 16:28:20 +00:00
|
|
|
[self.activeElement use];
|
2012-01-19 16:40:39 +00:00
|
|
|
|
2012-01-05 00:44:15 +00:00
|
|
|
[self.searchDisplayController setActive:NO animated:YES];
|
2012-01-19 16:40:39 +00:00
|
|
|
self.searchDisplayController.searchBar.text = self.activeElement.name;
|
|
|
|
|
|
|
|
[self updateAnimated:YES];
|
2012-01-05 00:44:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
|
|
|
|
|
|
|
|
[self updateAnimated:YES];
|
2011-11-30 21:42:40 +00:00
|
|
|
}
|
|
|
|
|
2012-01-19 16:40:39 +00:00
|
|
|
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
|
|
|
|
|
|
|
if (textField == self.contentField)
|
|
|
|
[self.contentField resignFirstResponder];
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
|
|
|
|
|
|
|
if (textField == self.contentField) {
|
|
|
|
self.contentField.enabled = NO;
|
2012-01-24 23:30:43 +00:00
|
|
|
if (![self.activeElement isKindOfClass:[OPElementStoredEntity class]])
|
|
|
|
// Not of a type whose content can be edited.
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ([((OPElementStoredEntity *) self.activeElement).contentObject isEqual:self.contentField.text])
|
|
|
|
// Content hasn't changed.
|
|
|
|
return;
|
|
|
|
|
|
|
|
[self updateElement:^{
|
|
|
|
((OPElementStoredEntity *) self.activeElement).contentObject = self.contentField.text;
|
|
|
|
}];
|
2012-01-19 16:40:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-30 21:42:40 +00:00
|
|
|
@end
|