Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

passcode lock support

using KKPasscodeLock
  • Loading branch information...
commit 774a4d0adfa6d2b44185a4e0b2ee7825e895f7f5 1 parent 0ea37da
@aporat aporat authored
View
40 AppSales.xcodeproj/project.pbxproj
@@ -7,6 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
+ 6D81575414CB3C2B005E8F3F /* KKKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D81574914CB3C2B005E8F3F /* KKKeychain.m */; };
+ 6D81575514CB3C2B005E8F3F /* KKPasscodeLock.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D81574B14CB3C2B005E8F3F /* KKPasscodeLock.m */; };
+ 6D81575614CB3C2B005E8F3F /* KKPasscodeSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D81574D14CB3C2B005E8F3F /* KKPasscodeSettingsViewController.m */; };
+ 6D81575714CB3C2B005E8F3F /* KKPasscodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D81574F14CB3C2B005E8F3F /* KKPasscodeViewController.m */; };
+ 6D81576514CB44BE005E8F3F /* passcode_square_empty.png in Resources */ = {isa = PBXBuildFile; fileRef = 6D81576114CB44BE005E8F3F /* passcode_square_empty.png */; };
+ 6D81576614CB44BE005E8F3F /* passcode_square_empty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6D81576214CB44BE005E8F3F /* passcode_square_empty@2x.png */; };
+ 6D81576714CB44BE005E8F3F /* passcode_square_filled.png in Resources */ = {isa = PBXBuildFile; fileRef = 6D81576314CB44BE005E8F3F /* passcode_square_filled.png */; };
+ 6D81576814CB44BE005E8F3F /* passcode_square_filled@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6D81576414CB44BE005E8F3F /* passcode_square_filled@2x.png */; };
7F06B6BF13BF73C8004DB170 /* Report.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F06B6BE13BF73C8004DB170 /* Report.m */; };
7F0B8A6913F77F95006E9151 /* PromoCodeOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F0B8A6813F77F95006E9151 /* PromoCodeOperation.m */; };
7F0B8A6E13F7B941006E9151 /* PromoCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F0B8A6D13F7B941006E9151 /* PromoCode.m */; };
@@ -637,6 +645,18 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
+ 6D81574814CB3C2B005E8F3F /* KKKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KKKeychain.h; sourceTree = "<group>"; };
+ 6D81574914CB3C2B005E8F3F /* KKKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KKKeychain.m; sourceTree = "<group>"; };
+ 6D81574A14CB3C2B005E8F3F /* KKPasscodeLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KKPasscodeLock.h; sourceTree = "<group>"; };
+ 6D81574B14CB3C2B005E8F3F /* KKPasscodeLock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KKPasscodeLock.m; sourceTree = "<group>"; };
+ 6D81574C14CB3C2B005E8F3F /* KKPasscodeSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KKPasscodeSettingsViewController.h; sourceTree = "<group>"; };
+ 6D81574D14CB3C2B005E8F3F /* KKPasscodeSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KKPasscodeSettingsViewController.m; sourceTree = "<group>"; };
+ 6D81574E14CB3C2B005E8F3F /* KKPasscodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KKPasscodeViewController.h; sourceTree = "<group>"; };
+ 6D81574F14CB3C2B005E8F3F /* KKPasscodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KKPasscodeViewController.m; sourceTree = "<group>"; };
+ 6D81576114CB44BE005E8F3F /* passcode_square_empty.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = passcode_square_empty.png; sourceTree = "<group>"; };
+ 6D81576214CB44BE005E8F3F /* passcode_square_empty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "passcode_square_empty@2x.png"; sourceTree = "<group>"; };
+ 6D81576314CB44BE005E8F3F /* passcode_square_filled.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = passcode_square_filled.png; sourceTree = "<group>"; };
+ 6D81576414CB44BE005E8F3F /* passcode_square_filled@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "passcode_square_filled@2x.png"; sourceTree = "<group>"; };
7F06B6BD13BF73C8004DB170 /* Report.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Report.h; sourceTree = "<group>"; };
7F06B6BE13BF73C8004DB170 /* Report.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Report.m; sourceTree = "<group>"; };
7F0B8A6713F77F95006E9151 /* PromoCodeOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PromoCodeOperation.h; sourceTree = "<group>"; };
@@ -1487,6 +1507,10 @@
7F709D6F13BCCB8B0008DBAD /* Images */ = {
isa = PBXGroup;
children = (
+ 6D81576114CB44BE005E8F3F /* passcode_square_empty.png */,
+ 6D81576214CB44BE005E8F3F /* passcode_square_empty@2x.png */,
+ 6D81576314CB44BE005E8F3F /* passcode_square_filled.png */,
+ 6D81576414CB44BE005E8F3F /* passcode_square_filled@2x.png */,
7F15452F14223FFA00D74A18 /* Default-Portrait~ipad.png */,
7F2E75D114237F2500793375 /* Default-Landscape~ipad.png */,
7FAEB66813E6D1AF00750788 /* Badge.png */,
@@ -1653,6 +1677,14 @@
7FDF601C13DD2107005F1E2D /* Helpers */ = {
isa = PBXGroup;
children = (
+ 6D81574814CB3C2B005E8F3F /* KKKeychain.h */,
+ 6D81574914CB3C2B005E8F3F /* KKKeychain.m */,
+ 6D81574A14CB3C2B005E8F3F /* KKPasscodeLock.h */,
+ 6D81574B14CB3C2B005E8F3F /* KKPasscodeLock.m */,
+ 6D81574C14CB3C2B005E8F3F /* KKPasscodeSettingsViewController.h */,
+ 6D81574D14CB3C2B005E8F3F /* KKPasscodeSettingsViewController.m */,
+ 6D81574E14CB3C2B005E8F3F /* KKPasscodeViewController.h */,
+ 6D81574F14CB3C2B005E8F3F /* KKPasscodeViewController.m */,
7F7F4A0313E4F4AD006C6651 /* JSONKit.h */,
7F7F4A0413E4F4AD006C6651 /* JSONKit.m */,
7F454F9E13D5C24F00AE47D1 /* UIColor+Extensions.h */,
@@ -2842,6 +2874,10 @@
7F0B8A7013F7C0EC006E9151 /* RequestPromoCode.png in Resources */,
7F15453014223FFA00D74A18 /* Default-Portrait~ipad.png in Resources */,
7F2E75D214237F2500793375 /* Default-Landscape~ipad.png in Resources */,
+ 6D81576514CB44BE005E8F3F /* passcode_square_empty.png in Resources */,
+ 6D81576614CB44BE005E8F3F /* passcode_square_empty@2x.png in Resources */,
+ 6D81576714CB44BE005E8F3F /* passcode_square_filled.png in Resources */,
+ 6D81576814CB44BE005E8F3F /* passcode_square_filled@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2910,6 +2946,10 @@
7F0B8A6E13F7B941006E9151 /* PromoCode.m in Sources */,
7F0B8A7313F7E647006E9151 /* PromoCodesLicenseViewController.m in Sources */,
7F26689714055D2200FD2CC5 /* AccountsViewController+VendorID.m in Sources */,
+ 6D81575414CB3C2B005E8F3F /* KKKeychain.m in Sources */,
+ 6D81575514CB3C2B005E8F3F /* KKPasscodeLock.m in Sources */,
+ 6D81575614CB3C2B005E8F3F /* KKPasscodeSettingsViewController.m in Sources */,
+ 6D81575714CB3C2B005E8F3F /* KKPasscodeViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
7 Classes/AccountsViewController.h
@@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
#import "FieldEditorViewController.h"
+#import "KKPasscodeSettingsViewController.h"
#define kAccountUsername @"username"
#define kAccountPassword @"password"
@@ -17,13 +18,17 @@
@class ASAccount;
@protocol AccountsViewControllerDelegate;
-@interface AccountsViewController : UITableViewController <NSFetchedResultsControllerDelegate, UIAlertViewDelegate, FieldEditorViewControllerDelegate>
+@interface AccountsViewController : UITableViewController <NSFetchedResultsControllerDelegate, UIAlertViewDelegate, FieldEditorViewControllerDelegate, KKPasscodeSettingsViewControllerDelegate>
{
id<AccountsViewControllerDelegate> delegate;
NSArray *accounts;
NSManagedObjectContext *managedObjectContext;
ASAccount *selectedAccount;
UIBarButtonItem *refreshButtonItem;
+
+ FieldSpecifier *passcodeLockField;
+ FieldEditorViewController *settingsViewController;
+ UINavigationController *settingsNavController;
}
@property (nonatomic, assign) id<AccountsViewControllerDelegate> delegate;
View
34 Classes/AccountsViewController.m
@@ -24,11 +24,13 @@
#import "AccountStatusView.h"
#import "PromoCodesViewController.h"
#import "PromoCodesLicenseViewController.h"
+#import "KKPasscodeLock.h"
#define kAddNewAccountEditorIdentifier @"AddNewAccountEditorIdentifier"
#define kEditAccountEditorIdentifier @"EditAccountEditorIdentifier"
#define kSettingsEditorIdentifier @"SettingsEditorIdentifier"
#define kUpdateExchangeRatesButton @"UpdateExchangeRatesButton"
+#define kPasscodeLockButton @"PasscodeLockButton"
#define kImportReportsButton @"ImportReportsButton"
#define kExportReportsButton @"ExportReportsButton"
#define kDownloadBoxcarButton @"DownloadBoxcarButton"
@@ -394,6 +396,13 @@ - (void)editAccount:(ASAccount *)account
- (void)showSettings
{
// main section
+ passcodeLockField = [FieldSpecifier buttonFieldWithKey:kPasscodeLockButton title:NSLocalizedString(@"Passcode Lock", nil)];
+ if ([[KKPasscodeLock sharedLock] isPasscodeRequired]) {
+ passcodeLockField.defaultValue = @"On";
+ } else {
+ passcodeLockField.defaultValue = @"Off";
+ }
+
NSString *baseCurrency = [[CurrencyManager sharedManager] baseCurrency];
NSArray *availableCurrencies = [[CurrencyManager sharedManager] availableCurrencies];
NSMutableArray *currencyFields = [NSMutableArray array];
@@ -408,7 +417,7 @@ - (void)showSettings
FieldSpecifier *currencySectionField = [FieldSpecifier subsectionFieldWithSection:currencySection key:@"currency"];
FieldSpecifier *updateExchangeRatesButtonField = [FieldSpecifier buttonFieldWithKey:kUpdateExchangeRatesButton title:NSLocalizedString(@"Update Exchange Rates Now", nil)];
FieldSpecifier *downloadPaymentsField = [FieldSpecifier switchFieldWithKey:kSettingDownloadPayments title:NSLocalizedString(@"Download Payments", nil) defaultValue:[[NSUserDefaults standardUserDefaults] boolForKey:kSettingDownloadPayments]];
- FieldSectionSpecifier *mainSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObjects:currencySectionField, updateExchangeRatesButtonField, downloadPaymentsField, nil]
+ FieldSectionSpecifier *mainSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObjects:passcodeLockField, currencySectionField, updateExchangeRatesButtonField, downloadPaymentsField, nil]
title:NSLocalizedString(@"General", nil)
description:NSLocalizedString(@"Exchange rates will automatically be refreshed periodically.", nil)];
@@ -442,12 +451,12 @@ - (void)showSettings
FieldSectionSpecifier *pushSectionFieldSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObject:pushSectionField] title:NSLocalizedString(@"Push Notifications", nil) description:nil];
NSArray *sections = [NSArray arrayWithObjects:mainSection, productsSection, pushSectionFieldSection, nil];
- FieldEditorViewController *settingsViewController = [[[FieldEditorViewController alloc] initWithFieldSections:sections title:NSLocalizedString(@"Settings",nil)] autorelease];
+ settingsViewController = [[[FieldEditorViewController alloc] initWithFieldSections:sections title:NSLocalizedString(@"Settings",nil)] autorelease];
settingsViewController.doneButtonTitle = NSLocalizedString(@"Done", nil);
settingsViewController.delegate = self;
settingsViewController.editorIdentifier = kSettingsEditorIdentifier;
- UINavigationController *settingsNavController = [[[UINavigationController alloc] initWithRootViewController:settingsViewController] autorelease];
+ settingsNavController = [[[UINavigationController alloc] initWithRootViewController:settingsViewController] autorelease];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
settingsNavController.modalPresentationStyle = UIModalPresentationFormSheet;
}
@@ -532,7 +541,12 @@ - (void)fieldEditor:(FieldEditorViewController *)editor didFinishEditingWithValu
- (void)fieldEditor:(FieldEditorViewController *)editor pressedButtonWithKey:(NSString *)key
{
- if ([key isEqualToString:kUpdateExchangeRatesButton]) {
+
+ if ([key isEqualToString:kPasscodeLockButton]) {
+ KKPasscodeSettingsViewController *vc = [[[KKPasscodeSettingsViewController alloc] initWithStyle:UITableViewStyleGrouped] autorelease];
+ vc.delegate = self;
+ [settingsNavController pushViewController:vc animated:YES];
+ } else if ([key isEqualToString:kUpdateExchangeRatesButton]) {
[[CurrencyManager sharedManager] forceRefresh];
} else if ([key isEqualToString:kImportReportsButton]) {
ASAccount *account = (ASAccount *)editor.context;
@@ -692,6 +706,18 @@ - (void)fieldEditorDidCancel:(FieldEditorViewController *)editor
[editor dismissModalViewControllerAnimated:YES];
}
+- (void)didSettingsChanged:(KKPasscodeSettingsViewController*)viewController
+{
+
+ if ([[KKPasscodeLock sharedLock] isPasscodeRequired]) {
+ passcodeLockField.defaultValue = @"On";
+ } else {
+ passcodeLockField.defaultValue = @"Off";
+ }
+
+ [settingsViewController.tableView reloadData];
+}
+
#pragma mark -
- (void)saveContext
View
3  Classes/AppSalesAppDelegate.h
@@ -8,10 +8,11 @@
#import <UIKit/UIKit.h>
#import "AccountsViewController.h"
+#import "KKPasscodeLock.h"
@class ASAccount;
-@interface AppSalesAppDelegate : NSObject <UIApplicationDelegate, UIActionSheetDelegate, AccountsViewControllerDelegate>
+@interface AppSalesAppDelegate : NSObject <UIApplicationDelegate, UIActionSheetDelegate, AccountsViewControllerDelegate, KKPasscodeViewControllerDelegate>
{
UIWindow *window;
View
27 Classes/AppSalesAppDelegate.m
@@ -26,6 +26,9 @@ @implementation AppSalesAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
+ [[KKPasscodeLock sharedLock] setDefaultSettings];
+ [[KKPasscodeLock sharedLock] setEraseOption:NO];
+
srandom(time(NULL));
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
@@ -205,6 +208,30 @@ - (void)applicationWillEnterForeground:(UIApplication *)application
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
+
+ if ([[KKPasscodeLock sharedLock] isPasscodeRequired]) {
+ KKPasscodeViewController *vc = [[[KKPasscodeViewController alloc] initWithNibName:nil bundle:nil] autorelease];
+ vc.mode = KKPasscodeModeEnter;
+ vc.delegate = self;
+
+ dispatch_async(dispatch_get_main_queue(),^ {
+ UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
+
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ nav.modalPresentationStyle = UIModalPresentationFormSheet;
+ nav.navigationBar.barStyle = UIBarStyleBlack;
+ nav.navigationBar.opaque = NO;
+ } else {
+ nav.navigationBar.tintColor = accountsViewController.navigationController.navigationBar.tintColor;
+ nav.navigationBar.translucent = accountsViewController.navigationController.navigationBar.translucent;
+ nav.navigationBar.opaque = accountsViewController.navigationController.navigationBar.opaque;
+ nav.navigationBar.barStyle = accountsViewController.navigationController.navigationBar.barStyle;
+ }
+
+ [accountsViewController presentModalViewController:nav animated:YES];
+ });
+
+ }
}
- (void)applicationWillTerminate:(UIApplication *)application
View
4 Classes/FieldEditorViewController.m
@@ -320,6 +320,9 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)ce
}
}
}
+ if (field.type == FieldSpecifierTypeButton) {
+ cell.detailTextLabel.text = field.defaultValue;
+ }
if (field.type == FieldSpecifierTypeButton && field.shouldDisplayDisclosureIndicator) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
@@ -576,7 +579,6 @@ + (FieldSpecifier *)buttonFieldWithKey:(NSString *)k title:(NSString *)buttonTit
{
FieldSpecifier *field = [FieldSpecifier fieldWithType:FieldSpecifierTypeButton key:k];
field.title = buttonTitle;
- field.defaultValue = @"";
return field;
}
View
27 Classes/KKKeychain.h
@@ -0,0 +1,27 @@
+//
+// Copyright 2011-2012 Kosher Penguin LLC
+// Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface KKKeychain : NSObject {
+}
+
++ (BOOL)setString:(NSString*)string forKey:(NSString*)key;
+
++ (NSString*)getStringForKey:(NSString*)key;
+
+@end
View
116 Classes/KKKeychain.m
@@ -0,0 +1,116 @@
+//
+// Copyright 2011-2012 Kosher Penguin LLC
+// Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "KKKeychain.h"
+#import <Security/Security.h>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+@implementation KKKeychain
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
++ (NSString*)appName {
+ NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+
+ NSString *appName = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
+ if (!appName) {
+ appName = [bundle objectForInfoDictionaryKey:@"CFBundleName"];
+ }
+ return appName;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
++ (BOOL)setString:(NSString*)string forKey:(NSString*)key {
+ if (string == nil || key == nil) {
+ return NO;
+ }
+
+ key = [NSString stringWithFormat:@"%@ - %@", [KKKeychain appName], key];
+
+ // First check if it already exists, by creating a search dictionary and requesting that
+ // nothing be returned, and performing the search anyway.
+ NSMutableDictionary *existsQueryDictionary = [NSMutableDictionary dictionary];
+
+ NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
+
+ [existsQueryDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
+
+ // Add the keys to the search dict
+ [existsQueryDictionary setObject:@"service" forKey:(id)kSecAttrService];
+ [existsQueryDictionary setObject:key forKey:(id)kSecAttrAccount];
+
+ OSStatus res = SecItemCopyMatching((CFDictionaryRef)existsQueryDictionary, NULL);
+ if (res == errSecItemNotFound) {
+ if (string != nil) {
+ NSMutableDictionary *addDict = existsQueryDictionary;
+ [addDict setObject:data forKey:(id)kSecValueData];
+
+ res = SecItemAdd((CFDictionaryRef)addDict, NULL);
+ NSAssert1(res == errSecSuccess, @"Recieved %d from SecItemAdd!", res);
+ }
+ } else if (res == errSecSuccess) {
+ // Modify an existing one
+ // Actually pull it now of the keychain at this point.
+ NSDictionary *attributeDict = [NSDictionary dictionaryWithObject:data forKey:(id)kSecValueData];
+
+ res = SecItemUpdate((CFDictionaryRef)existsQueryDictionary, (CFDictionaryRef)attributeDict);
+ NSAssert1(res == errSecSuccess, @"SecItemUpdated returned %d!", res);
+
+ } else {
+ NSAssert1(NO, @"Received %d from SecItemCopyMatching!", res);
+ }
+
+ return YES;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
++ (NSString*)getStringForKey:(NSString*)key {
+
+ key = [NSString stringWithFormat:@"%@ - %@", [KKKeychain appName], key];
+
+ NSMutableDictionary *existsQueryDictionary = [NSMutableDictionary dictionary];
+
+ [existsQueryDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
+
+ // Add the keys to the search dict
+ [existsQueryDictionary setObject:@"service" forKey:(id)kSecAttrService];
+ [existsQueryDictionary setObject:key forKey:(id)kSecAttrAccount];
+
+ // We want the data back!
+ NSData *data = nil;
+
+ [existsQueryDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
+
+ OSStatus res = SecItemCopyMatching((CFDictionaryRef)existsQueryDictionary, (CFTypeRef *)&data);
+ [data autorelease];
+ if (res == errSecSuccess) {
+ NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
+ return string;
+ } else {
+ NSAssert1(res == errSecItemNotFound, @"SecItemCopyMatching returned %d!", res);
+ }
+
+ return nil;
+}
+
+
+
+@end
View
35 Classes/KKPasscodeLock.h
@@ -0,0 +1,35 @@
+//
+// Copyright 2011-2012 Kosher Penguin LLC
+// Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+#import "KKPasscodeViewController.h"
+
+@interface KKPasscodeLock : NSObject {
+ BOOL _eraseOption;
+ NSUInteger _attemptsAllowed;
+}
+
++ (KKPasscodeLock*)sharedLock;
+
+- (BOOL)isPasscodeRequired;
+
+- (void)setDefaultSettings;
+
+@property (nonatomic,assign) BOOL eraseOption;
+@property (nonatomic,assign) NSUInteger attemptsAllowed;
+
+@end
View
69 Classes/KKPasscodeLock.m
@@ -0,0 +1,69 @@
+//
+// Copyright 2011-2012 Kosher Penguin LLC
+// Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "KKPasscodeLock.h"
+#import "KKKeychain.h"
+#import "KKPasscodeViewController.h"
+
+static KKPasscodeLock *sharedLock = nil;
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+@implementation KKPasscodeLock
+
+
+@synthesize eraseOption = _eraseOption;
+@synthesize attemptsAllowed = _attemptsAllowed;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
++ (KKPasscodeLock*)sharedLock
+{
+ @synchronized(self) {
+ if (sharedLock == nil) {
+ sharedLock = [[self alloc] init];
+ sharedLock.eraseOption = YES;
+ sharedLock.attemptsAllowed = 5;
+ }
+ }
+ return sharedLock;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (BOOL)isPasscodeRequired
+{
+ return [[KKKeychain getStringForKey:@"passcode_on"] isEqualToString:@"YES"];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)setDefaultSettings
+{
+ if (![KKKeychain getStringForKey:@"passcode_on"]) {
+ [KKKeychain setString:@"NO" forKey:@"passcode_on"];
+ }
+
+ if (![KKKeychain getStringForKey:@"erase_data_on"]) {
+ [KKKeychain setString:@"NO" forKey:@"erase_data_on"];
+ }
+}
+
+
+
+@end
View
43 Classes/KKPasscodeSettingsViewController.h
@@ -0,0 +1,43 @@
+//
+// Copyright 2011-2012 Kosher Penguin LLC
+// Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <UIKit/UIKit.h>
+#import "KKPasscodeViewController.h"
+
+@class KKPasscodeSettingsViewController;
+
+@protocol KKPasscodeSettingsViewControllerDelegate <NSObject>
+
+@optional
+
+- (void)didSettingsChanged:(KKPasscodeSettingsViewController*)viewController;
+
+@end
+
+@interface KKPasscodeSettingsViewController : UITableViewController <UIActionSheetDelegate, KKPasscodeViewControllerDelegate> {
+
+ id <KKPasscodeSettingsViewControllerDelegate> _delegate;
+
+ UISwitch* _eraseDataSwitch;
+
+ BOOL _passcodeLockOn;
+ BOOL _eraseDataOn;
+}
+
+@property (nonatomic, assign) id <KKPasscodeSettingsViewControllerDelegate> delegate;
+
+@end
View
282 Classes/KKPasscodeSettingsViewController.m
@@ -0,0 +1,282 @@
+//
+// Copyright 2011-2012 Kosher Penguin LLC
+// Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "KKPasscodeSettingsViewController.h"
+#import "KKKeychain.h"
+#import "KKPasscodeViewController.h"
+#import "KKPasscodeLock.h"
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+@implementation KKPasscodeSettingsViewController
+
+
+@synthesize delegate = _delegate;
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark UIViewController
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+ self.navigationItem.title = @"Passcode Lock";
+
+ _eraseDataSwitch = [[UISwitch alloc] init];
+ [_eraseDataSwitch addTarget:self action:@selector(eraseDataSwitchChanged:) forControlEvents:UIControlEventValueChanged];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+ _passcodeLockOn = [[KKKeychain getStringForKey:@"passcode_on"] isEqualToString:@"YES"];
+ _eraseDataOn = [[KKKeychain getStringForKey:@"erase_data_on"] isEqualToString:@"YES"];
+ _eraseDataSwitch.on = _eraseDataOn;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
+{
+ return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark UIActionSheetDelegate
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
+{
+ if (buttonIndex == 0) {
+ _eraseDataOn = YES;
+ [KKKeychain setString:@"YES" forKey:@"erase_data_on"];
+ } else {
+ _eraseDataOn = NO;
+ [KKKeychain setString:@"NO" forKey:@"erase_data_on"];
+ }
+ [_eraseDataSwitch setOn:_eraseDataOn animated:YES];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)eraseDataSwitchChanged:(id)sender {
+ if (_eraseDataSwitch.on) {
+ NSString* title = [NSString stringWithFormat:@"All data in this app will be erased after %d failed passcode attempts.", [[KKPasscodeLock sharedLock] attemptsAllowed]];
+
+ UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:title delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Enable" otherButtonTitles:nil];
+ [sheet showInView:self.view];
+ [sheet release];
+ } else {
+ _eraseDataOn = NO;
+ [KKKeychain setString:@"NO" forKey:@"erase_data_on"];
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark Table view data source
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+ if ([[KKPasscodeLock sharedLock] eraseOption]) {
+ return 3;
+ }
+
+ return 2;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+ return 1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
+{
+ if (section == 2) {
+ return [NSString stringWithFormat:@"Erase all data in this app after %d failed passcode attempts.", [[KKPasscodeLock sharedLock] attemptsAllowed]];;
+ } else {
+ return @"";
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (UITableViewCell *)tableView:(UITableView *)tableView
+ cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+
+ static NSString *CellIdentifier = @"Cell";
+
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+ }
+
+ if (indexPath.section == 0) {
+ if (_passcodeLockOn) {
+ cell.textLabel.text = @"Turn Passcode Off";
+ } else {
+ cell.textLabel.text = @"Turn Passcode On";
+ }
+ cell.textLabel.textColor = [UIColor blackColor];
+ cell.textLabel.textAlignment = UITextAlignmentCenter;
+ cell.accessoryView = nil;
+ cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+ } else if (indexPath.section == 1) {
+ cell.textLabel.text = @"Change Passcode";
+ if (_passcodeLockOn) {
+ cell.textLabel.textColor = [UIColor blackColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleBlue;
+ } else {
+ cell.textLabel.textColor = [UIColor grayColor];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ }
+ cell.textLabel.textAlignment = UITextAlignmentCenter;
+ cell.accessoryView = nil;
+ } else if (indexPath.section == 2) {
+ cell.textLabel.text = @"Erase Data";
+ cell.textLabel.textAlignment = UITextAlignmentLeft;
+ cell.accessoryView = _eraseDataSwitch;
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ if (_passcodeLockOn) {
+ cell.textLabel.textColor = [UIColor blackColor];
+ _eraseDataSwitch.enabled = YES;
+ } else {
+ cell.textLabel.textColor = [UIColor grayColor];
+ _eraseDataSwitch.enabled = NO;
+ }
+ }
+
+ return cell;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark Table view delegate
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ if (indexPath.section == 0) {
+ KKPasscodeViewController *vc = [[[KKPasscodeViewController alloc] initWithNibName:nil
+ bundle:nil] autorelease];
+ vc.delegate = self;
+
+ if (_passcodeLockOn) {
+ vc.mode = KKPasscodeModeDisabled;
+ } else {
+ vc.mode = KKPasscodeModeSet;
+ }
+
+ UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:vc] autorelease];
+
+
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ nav.modalPresentationStyle = UIModalPresentationFormSheet;
+ nav.navigationBar.barStyle = UIBarStyleBlack;
+ nav.navigationBar.opaque = NO;
+ } else {
+ nav.navigationBar.tintColor = self.navigationController.navigationBar.tintColor;
+ nav.navigationBar.translucent = self.navigationController.navigationBar.translucent;
+ nav.navigationBar.opaque = self.navigationController.navigationBar.opaque;
+ nav.navigationBar.barStyle = self.navigationController.navigationBar.barStyle;
+ }
+
+ [self.navigationController presentModalViewController:nav animated:YES];
+
+
+ } else if (indexPath.section == 1 && _passcodeLockOn) {
+ KKPasscodeViewController *vc = [[[KKPasscodeViewController alloc] initWithNibName:nil bundle:nil] autorelease];
+ vc.delegate = self;
+
+ vc.mode = KKPasscodeModeChange;
+
+ UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:vc] autorelease];
+
+
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ nav.modalPresentationStyle = UIModalPresentationFormSheet;
+ nav.navigationBar.barStyle = UIBarStyleBlack;
+ nav.navigationBar.opaque = NO;
+ } else {
+ nav.navigationBar.tintColor = self.navigationController.navigationBar.tintColor;
+ nav.navigationBar.translucent = self.navigationController.navigationBar.translucent;
+ nav.navigationBar.opaque = self.navigationController.navigationBar.opaque;
+ nav.navigationBar.barStyle = self.navigationController.navigationBar.barStyle;
+ }
+
+ [self.navigationController presentModalViewController:nav animated:YES];
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)didSettingsChanged:(KKPasscodeViewController*)viewController {
+ _passcodeLockOn = [[KKKeychain getStringForKey:@"passcode_on"] isEqualToString:@"YES"];
+ _eraseDataOn = [[KKKeychain getStringForKey:@"erase_data_on"] isEqualToString:@"YES"];
+ _eraseDataSwitch.on = _eraseDataOn;
+
+ [self.tableView reloadData];
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark Memory management
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)dealloc
+{
+ [_eraseDataSwitch release];
+
+ [super dealloc];
+}
+
+
+@end
+
View
90 Classes/KKPasscodeViewController.h
@@ -0,0 +1,90 @@
+//
+// Copyright 2011-2012 Kosher Penguin LLC
+// Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <UIKit/UIKit.h>
+
+
+enum {
+ KKPasscodeModeEnter = 0,
+ KKPasscodeModeSet = 1,
+ KKPasscodeModeDisabled = 2,
+ KKPasscodeModeChange = 3
+};
+typedef NSUInteger KKPasscodeMode;
+
+
+@class KKPasscodeViewController;
+
+@protocol KKPasscodeViewControllerDelegate <NSObject>
+
+@optional
+
+- (void)didPasscodeEnteredCorrectly:(KKPasscodeViewController*)viewController;
+
+- (void)didPasscodeEnteredIncorrectly:(KKPasscodeViewController*)viewController;
+
+- (void)shouldEraseApplicationData:(KKPasscodeViewController*)viewController;
+
+- (void)didSettingsChanged:(KKPasscodeViewController*)viewController;
+
+@end
+
+
+
+@interface KKPasscodeViewController : UIViewController <UITextFieldDelegate,
+ UITableViewDelegate,
+ UITableViewDataSource> {
+
+
+ id<KKPasscodeViewControllerDelegate> _delegate;
+
+ UILabel* _passcodeConfirmationWarningLabel;
+ UIView* _failedAttemptsView;
+ UILabel* _failedAttemptsLabel;
+ NSInteger _failedAttemptsCount;
+
+ NSUInteger _tableIndex;
+ NSMutableArray* _tableViews;
+ NSMutableArray* _textFields;
+ NSMutableArray* _squares;
+
+ UITableView* _enterPasscodeTableView;
+ UITextField* _enterPasscodeTextField;
+ NSArray* _enterPasscodeSquareImageViews;
+
+ UITableView* _setPasscodeTableView;
+ UITextField* _setPasscodeTextField;
+ NSArray* _setPasscodeSquareImageViews;
+
+ UITableView* _confirmPasscodeTableView;
+ UITextField* _confirmPasscodeTextField;
+ NSArray* _confirmPasscodeSquareImageViews;
+
+ KKPasscodeMode _mode;
+
+ BOOL _passcodeLockOn;
+ BOOL _eraseData;
+
+ CGFloat _viewWidth;
+}
+
+@property (nonatomic, assign) id <KKPasscodeViewControllerDelegate> delegate;
+@property (nonatomic, assign) KKPasscodeMode mode;
+
+
+@end
+
View
828 Classes/KKPasscodeViewController.m
@@ -0,0 +1,828 @@
+//
+// Copyright 2011-2012 Kosher Penguin LLC
+// Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "KKPasscodeViewController.h"
+#import "KKKeychain.h"
+#import "KKPasscodeSettingsViewController.h"
+#import "KKPasscodeLock.h"
+#import <QuartzCore/QuartzCore.h>
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+@interface KKPasscodeViewController(Private)
+
+- (UITextField*)allocAndInitPasscodeTextField;
+- (NSArray*)squares;
+- (UIView*)passwordHeaderViewForTextField:(UITextField*)textField;
+- (void)moveToNextTableView;
+- (void)moveToPreviousTableView;
+
+@end
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+@implementation KKPasscodeViewController
+
+@synthesize delegate = _delegate;
+@synthesize mode;
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark UIViewController
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+ if (self == [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
+ self.modalPresentationStyle = UIModalPresentationFormSheet;
+
+ _enterPasscodeTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
+ _enterPasscodeTableView.delegate = self;
+ _enterPasscodeTableView.dataSource = self;
+ _enterPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ _enterPasscodeTableView.backgroundColor = [UIColor groupTableViewBackgroundColor];
+ [self.view addSubview:_enterPasscodeTableView];
+
+ _setPasscodeTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
+ _setPasscodeTableView.delegate = self;
+ _setPasscodeTableView.dataSource = self;
+ _setPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ _setPasscodeTableView.backgroundColor = [UIColor groupTableViewBackgroundColor];
+ [self.view addSubview:_setPasscodeTableView];
+
+ _confirmPasscodeTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
+ _confirmPasscodeTableView.delegate = self;
+ _confirmPasscodeTableView.dataSource = self;
+ _confirmPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ _confirmPasscodeTableView.backgroundColor = [UIColor groupTableViewBackgroundColor];
+ [self.view addSubview:_confirmPasscodeTableView];
+ }
+
+ return self;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
+{
+ return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ _passcodeLockOn = [[KKKeychain getStringForKey:@"passcode_on"] isEqualToString:@"YES"];
+ _eraseData = [[KKPasscodeLock sharedLock] eraseOption] && [[KKKeychain getStringForKey:@"erase_data_on"] isEqualToString:@"YES"];
+
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ _viewWidth = 540.0f;
+ } else {
+ _viewWidth = 320.0f;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
+
+ _enterPasscodeTextField = [self allocAndInitPasscodeTextField];
+ _setPasscodeTextField = [self allocAndInitPasscodeTextField];
+ _confirmPasscodeTextField = [self allocAndInitPasscodeTextField];
+
+ _tableViews = [[NSMutableArray alloc] init];
+ _textFields = [[NSMutableArray alloc] init];
+ _squares = [[NSMutableArray alloc] init];
+
+ if (mode == KKPasscodeModeSet || mode == KKPasscodeModeChange) {
+ if (_passcodeLockOn) {
+ _enterPasscodeTableView.tableHeaderView = [self passwordHeaderViewForTextField:_enterPasscodeTextField];
+ [_tableViews addObject:_enterPasscodeTableView];
+ [_textFields addObject:_enterPasscodeTextField];
+ [_squares addObject:[self squares]];
+ for (int i = 0; i < [[_squares lastObject] count]; i++) {
+ [_enterPasscodeTableView.tableHeaderView addSubview:[[_squares lastObject] objectAtIndex:i]];
+ }
+ }
+
+ _setPasscodeTableView.tableHeaderView = [self passwordHeaderViewForTextField:_setPasscodeTextField];
+ [_tableViews addObject:_setPasscodeTableView];
+ [_textFields addObject:_setPasscodeTextField];
+ [_squares addObject:[self squares]];
+ for (int i = 0; i < [[_squares lastObject] count]; i++) {
+ [_setPasscodeTableView.tableHeaderView addSubview:[[_squares lastObject] objectAtIndex:i]];
+ }
+ _confirmPasscodeTableView.tableHeaderView = [self passwordHeaderViewForTextField:_confirmPasscodeTextField];
+ [_tableViews addObject:_confirmPasscodeTableView];
+ [_textFields addObject:_confirmPasscodeTextField];
+ [_squares addObject:[self squares]];
+ for (int i = 0; i < [[_squares lastObject] count]; i++) {
+ [_confirmPasscodeTableView.tableHeaderView addSubview:[[_squares lastObject] objectAtIndex:i]];
+ }
+ } else {
+ _enterPasscodeTableView.tableHeaderView = [self passwordHeaderViewForTextField:_enterPasscodeTextField];
+ [_tableViews addObject:_enterPasscodeTableView];
+ [_textFields addObject:_enterPasscodeTextField];
+ [_squares addObject:[self squares]];
+ for (int i = 0; i < [[_squares lastObject] count]; i++) {
+ [_enterPasscodeTableView.tableHeaderView addSubview:[[_squares lastObject] objectAtIndex:i]];
+ }
+ }
+
+ [self.view addSubview:[_tableViews objectAtIndex:0]];
+
+ // shift any extra table views away
+ for (int i = 1; i < [_tableViews count]; i++) {
+ UITableView *tableView = [_tableViews objectAtIndex:i];
+ tableView.frame = CGRectMake(tableView.frame.origin.x + _viewWidth, tableView.frame.origin.y, tableView.frame.size.width, tableView.frame.size.height);
+ [self.view addSubview:tableView];
+ }
+
+
+ [[_textFields objectAtIndex:0] becomeFirstResponder];
+ [[_tableViews objectAtIndex:0] reloadData];
+ [[_textFields objectAtIndex:[_tableViews count] - 1] setReturnKeyType:UIReturnKeyDone];
+
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ if ([_tableViews count] > 1) {
+ [self moveToNextTableView];
+ [self moveToPreviousTableView];
+ } else {
+ UITableView *tv = [_tableViews objectAtIndex:0];
+ tv.frame = CGRectMake(tv.frame.origin.x, tv.frame.origin.y, 768.0, 960.0);
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark Private
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (UITextField*)allocAndInitPasscodeTextField
+{
+ UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(29.0, 18.0, 271.0, 24.0)];
+ textField.font = [UIFont systemFontOfSize:14];
+ textField.text = @"";
+ textField.textColor = [UIColor blackColor];
+ textField.secureTextEntry = YES;
+ textField.delegate = self;
+
+ textField.keyboardAppearance = UIKeyboardAppearanceAlert;
+
+ return textField;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)cancelButtonPressed:(id)sender
+{
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)incrementAndShowFailedAttemptsLabel
+{
+ _enterPasscodeTextField.text = @"";
+ for (int i = 0; i < 4; i++) {
+ [[[_squares objectAtIndex:_tableIndex] objectAtIndex:i] setImage:[UIImage imageNamed:@"passcode_square_empty.png"]];
+ }
+
+ _failedAttemptsCount += 1;
+ if (_failedAttemptsCount == 1) {
+ _failedAttemptsLabel.text = @"1 Failed Passcode Attempt";
+ } else {
+ _failedAttemptsLabel.text = [NSString stringWithFormat:@"%i Failed Passcode Attempts", _failedAttemptsCount];
+ }
+ CGSize size = [_failedAttemptsLabel.text sizeWithFont:[UIFont boldSystemFontOfSize:14.0]];
+ _failedAttemptsView.frame = CGRectMake((_viewWidth - (size.width + 36.0)) / 2, 147.5, size.width + 36.0, size.height + 10.0);
+ _failedAttemptsLabel.frame = CGRectMake((_viewWidth - (size.width + 36.0)) / 2, 147.5, size.width + 36.0, size.height + 10.0);
+
+ CAGradientLayer *gradient = [CAGradientLayer layer];
+ gradient.frame = _failedAttemptsView.bounds;
+ gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithRed:0.714 green:0.043 blue:0.043 alpha:1.0] CGColor],
+ (id)[[UIColor colorWithRed:0.761 green:0.192 blue:0.192 alpha:1.0] CGColor], nil];
+ [_failedAttemptsView.layer insertSublayer:gradient atIndex:0];
+ _failedAttemptsView.layer.masksToBounds = YES;
+
+ _failedAttemptsLabel.hidden = NO;
+ _failedAttemptsView.hidden = NO;
+
+ if (_failedAttemptsCount == [[KKPasscodeLock sharedLock] attemptsAllowed]) {
+
+ _enterPasscodeTextField.delegate = nil;
+
+ if (_eraseData) {
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ [UIView beginAnimations:@"fadeIn" context:nil];
+ [UIView setAnimationDelay:0.25];
+ [UIView setAnimationDuration:0.5];
+
+ [UIView commitAnimations];
+ }
+
+ if ([_delegate respondsToSelector:@selector(shouldEraseApplicationData:)]) {
+ [_delegate shouldEraseApplicationData:self];
+ }
+ } else {
+ if ([_delegate respondsToSelector:@selector(didPasscodeEnteredIncorrectly:)]) {
+ [_delegate didPasscodeEnteredIncorrectly:self];
+ }
+ }
+ }
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)moveToNextTableView
+{
+ _tableIndex += 1;
+ UITableView *oldTableView = [_tableViews objectAtIndex:_tableIndex - 1];
+ UITableView *newTableView = [_tableViews objectAtIndex:_tableIndex];
+ newTableView.frame = CGRectMake(oldTableView.frame.origin.x + _viewWidth, oldTableView.frame.origin.y, oldTableView.frame.size.width, oldTableView.frame.size.height);
+
+ for (int i = 0; i < 4; i++) {
+ [[[_squares objectAtIndex:_tableIndex] objectAtIndex:i] setImage:[UIImage imageNamed:@"passcode_square_empty.png"]];
+ }
+
+ [UIView beginAnimations:@"" context:nil];
+ [UIView setAnimationDuration:0.25];
+ oldTableView.frame = CGRectMake(oldTableView.frame.origin.x - _viewWidth, oldTableView.frame.origin.y, oldTableView.frame.size.width, oldTableView.frame.size.height);
+ newTableView.frame = self.view.frame;
+ [UIView commitAnimations];
+
+
+ [[_textFields objectAtIndex:_tableIndex - 1] resignFirstResponder];
+ [[_textFields objectAtIndex:_tableIndex] becomeFirstResponder];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)moveToPreviousTableView
+{
+ _tableIndex -= 1;
+ UITableView *oldTableView = [_tableViews objectAtIndex:_tableIndex + 1];
+ UITableView *newTableView = [_tableViews objectAtIndex:_tableIndex];
+ newTableView.frame = CGRectMake(oldTableView.frame.origin.x - _viewWidth, oldTableView.frame.origin.y, oldTableView.frame.size.width, oldTableView.frame.size.height);
+
+ for (int i = 0; i < 4; i++) {
+ [[[_squares objectAtIndex:_tableIndex] objectAtIndex:i] setImage:[UIImage imageNamed:@"passcode_square_empty.png"]];
+ }
+
+ [UIView beginAnimations:@"" context:nil];
+ [UIView setAnimationDuration:0.25];
+ oldTableView.frame = CGRectMake(oldTableView.frame.origin.x + _viewWidth, oldTableView.frame.origin.y, oldTableView.frame.size.width, oldTableView.frame.size.height);
+ newTableView.frame = self.view.frame;
+ [UIView commitAnimations];
+
+
+
+ [[_textFields objectAtIndex:_tableIndex + 1] resignFirstResponder];
+ [[_textFields objectAtIndex:_tableIndex] becomeFirstResponder];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)nextDigitPressed
+{
+
+ UITextField *textField = [_textFields objectAtIndex:_tableIndex];
+
+ if (![textField.text isEqualToString:@""]) {
+
+ if (mode == KKPasscodeModeSet) {
+ if ([textField isEqual:_setPasscodeTextField]) {
+ [self moveToNextTableView];
+ } else if ([textField isEqual:_confirmPasscodeTextField]) {
+ if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
+ _confirmPasscodeTextField.text = @"";
+ _setPasscodeTextField.text = @"";
+ _passcodeConfirmationWarningLabel.text = @"Passcodes did not match. Try again.";
+ [self moveToPreviousTableView];
+ } else {
+ if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
+ [KKKeychain setString:@"YES" forKey:@"passcode_on"];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ }
+ } else if (mode == KKPasscodeModeChange) {
+ NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
+ if ([textField isEqual:_enterPasscodeTextField]) {
+ if ([passcode isEqualToString:_enterPasscodeTextField.text]) {
+ [self moveToNextTableView];
+ } else {
+ [self incrementAndShowFailedAttemptsLabel];
+ }
+ } else if ([textField isEqual:_setPasscodeTextField]) {
+ if ([passcode isEqualToString:_setPasscodeTextField.text]) {
+ _setPasscodeTextField.text = @"";
+ _passcodeConfirmationWarningLabel.text = @"Enter a different passcode. You cannot re-use the same passcode.";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 131.5, _viewWidth, 60.0);
+ } else {
+ _passcodeConfirmationWarningLabel.text = @"";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 146.5, _viewWidth, 30.0);
+ [self moveToNextTableView];
+ }
+ } else if ([textField isEqual:_confirmPasscodeTextField]) {
+ if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
+ _confirmPasscodeTextField.text = @"";
+ _setPasscodeTextField.text = @"";
+ _passcodeConfirmationWarningLabel.text = @"Passcodes did not match. Try again.";
+ [self moveToPreviousTableView];
+ } else {
+ if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
+ [KKKeychain setString:@"YES" forKey:@"passcode_on"];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ }
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)doneButtonPressed
+{
+
+ UITextField *textField = [_textFields objectAtIndex:_tableIndex];
+
+ if (mode == KKPasscodeModeEnter) {
+ NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
+ if ([_enterPasscodeTextField.text isEqualToString:passcode]) {
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ [UIView beginAnimations:@"fadeIn" context:nil];
+ [UIView setAnimationDelay:0.25];
+ [UIView setAnimationDuration:0.5];
+
+ [UIView commitAnimations];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didPasscodeEnteredCorrectly::)]) {
+ [_delegate didPasscodeEnteredCorrectly:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ } else {
+ [self incrementAndShowFailedAttemptsLabel];
+ }
+ } else if (mode == KKPasscodeModeSet) {
+ if ([textField isEqual:_setPasscodeTextField]) {
+ [self moveToNextTableView];
+ } else if ([textField isEqual:_confirmPasscodeTextField]) {
+ if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
+ _confirmPasscodeTextField.text = @"";
+ _setPasscodeTextField.text = @"";
+ _passcodeConfirmationWarningLabel.text = @"Passcodes did not match. Try again.";
+ [self moveToPreviousTableView];
+ } else {
+ if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
+ [KKKeychain setString:@"YES" forKey:@"passcode_on"];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ }
+ } else if (mode == KKPasscodeModeChange) {
+ NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
+ if ([textField isEqual:_enterPasscodeTextField]) {
+ if ([passcode isEqualToString:_enterPasscodeTextField.text]) {
+ [self moveToNextTableView];
+ } else {
+ [self incrementAndShowFailedAttemptsLabel];
+ }
+ } else if ([textField isEqual:_setPasscodeTextField]) {
+ if ([passcode isEqualToString:_setPasscodeTextField.text]) {
+ _setPasscodeTextField.text = @"";
+ _passcodeConfirmationWarningLabel.text = @"Enter a different passcode. Cannot re-use the same passcode.";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 131.5, _viewWidth, 60.0);
+ } else {
+ _passcodeConfirmationWarningLabel.text = @"";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 146.5, _viewWidth, 30.0);
+ [self moveToNextTableView];
+ }
+ } else if ([textField isEqual:_confirmPasscodeTextField]) {
+ if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
+ _confirmPasscodeTextField.text = @"";
+ _setPasscodeTextField.text = @"";
+ _passcodeConfirmationWarningLabel.text = @"Passcodes did not match. Try again.";
+ [self moveToPreviousTableView];
+ } else {
+ if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
+ [KKKeychain setString:@"YES" forKey:@"passcode_on"];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ }
+ } else if (mode == KKPasscodeModeDisabled) {
+ NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
+ if ([_enterPasscodeTextField.text isEqualToString:passcode]) {
+ if ([KKKeychain setString:@"NO" forKey:@"passcode_on"]) {
+ [KKKeychain setString:@"" forKey:@"passcode"];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ } else {
+ [self incrementAndShowFailedAttemptsLabel];
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (UIView*)passwordHeaderViewForTextField:(UITextField*)textField
+{
+
+ textField.keyboardType = UIKeyboardTypeNumberPad;
+
+ textField.hidden = YES;
+ [self.view addSubview:textField];
+
+ UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, _viewWidth, 70.0)] autorelease];
+ UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 27.5, _viewWidth, 30.0)];
+ headerLabel.textColor = [UIColor colorWithRed:0.298 green:0.337 blue:0.424 alpha:1.0];
+ headerLabel.backgroundColor = [UIColor clearColor];
+ headerLabel.textAlignment = UITextAlignmentCenter;
+ headerLabel.font = [UIFont boldSystemFontOfSize:17.0];
+ headerLabel.shadowOffset = CGSizeMake(0, 1.0);
+ headerLabel.shadowColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
+
+ if ([textField isEqual:_setPasscodeTextField]) {
+ _passcodeConfirmationWarningLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 146.5, _viewWidth, 30.0)];
+ _passcodeConfirmationWarningLabel.textColor = [UIColor colorWithRed:0.298 green:0.337 blue:0.424 alpha:1.0];
+ _passcodeConfirmationWarningLabel.backgroundColor = [UIColor clearColor];
+ _passcodeConfirmationWarningLabel.textAlignment = UITextAlignmentCenter;
+ _passcodeConfirmationWarningLabel.font = [UIFont systemFontOfSize:14.0];
+ _passcodeConfirmationWarningLabel.shadowOffset = CGSizeMake(0, 1.0);
+ _passcodeConfirmationWarningLabel.shadowColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
+ _passcodeConfirmationWarningLabel.text = @"";
+ _passcodeConfirmationWarningLabel.numberOfLines = 0;
+ _passcodeConfirmationWarningLabel.lineBreakMode = UILineBreakModeWordWrap;
+ [headerView addSubview:_passcodeConfirmationWarningLabel];
+ }
+
+ if ([textField isEqual:_enterPasscodeTextField]) {
+
+ NSString *text = @"1 Failed Passcode Attempt";
+ CGSize size = [text sizeWithFont:[UIFont boldSystemFontOfSize:14.0]];
+ _failedAttemptsView = [[UIView alloc] initWithFrame:CGRectMake((_viewWidth - (size.width + 36.0)) / 2, 147.5, size.width + 36.0, size.height + 10.0)];
+ _failedAttemptsLabel = [[UILabel alloc] initWithFrame:CGRectMake((_viewWidth - (size.width + 36.0)) / 2, 147.5, size.width + 36.0, size.height + 10.0)];
+ _failedAttemptsLabel.backgroundColor = [UIColor clearColor];
+ _failedAttemptsLabel.textColor = [UIColor whiteColor];
+ _failedAttemptsLabel.text = text;
+ _failedAttemptsLabel.font = [UIFont boldSystemFontOfSize:14.0];
+ _failedAttemptsLabel.textAlignment = UITextAlignmentCenter;
+ _failedAttemptsLabel.shadowOffset = CGSizeMake(0, -1.0);
+ _failedAttemptsLabel.shadowColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0];
+ _failedAttemptsView.layer.cornerRadius = 14;
+ _failedAttemptsView.layer.borderWidth = 1.0;
+ _failedAttemptsView.layer.borderColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.25] CGColor];
+
+ _failedAttemptsLabel.hidden = YES;
+ _failedAttemptsView.hidden = YES;
+
+ CAGradientLayer *gradient = [CAGradientLayer layer];
+ gradient.frame = _failedAttemptsView.bounds;
+ gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithRed:0.714 green:0.043 blue:0.043 alpha:1.0] CGColor],
+ (id)[[UIColor colorWithRed:0.761 green:0.192 blue:0.192 alpha:1.0] CGColor], nil];
+ [_failedAttemptsView.layer insertSublayer:gradient atIndex:1];
+ _failedAttemptsView.layer.masksToBounds = YES;
+
+ [headerView addSubview:_failedAttemptsView];
+ [headerView addSubview:_failedAttemptsLabel];
+
+ [_failedAttemptsView release];
+ [_failedAttemptsLabel release];
+ }
+
+ if (mode == KKPasscodeModeSet) {
+ self.navigationItem.title = @"Set Passcode";
+ UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelButtonPressed:)];
+ self.navigationItem.leftBarButtonItem = cancel;
+ [cancel release];
+
+
+ if ([textField isEqual:_enterPasscodeTextField]) {
+ headerLabel.text = @"Enter your passcode";
+ } else if ([textField isEqual:_setPasscodeTextField]) {
+ headerLabel.text = @"Enter a passcode";
+ UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelButtonPressed:)];
+ self.navigationItem.leftBarButtonItem = cancel;
+ [cancel release];
+
+ } else if ([textField isEqual:_confirmPasscodeTextField]) {
+ headerLabel.text = @"Re-enter your passcode";
+ }
+ } else if (mode == KKPasscodeModeDisabled) {
+ self.navigationItem.title = @"Turn off Passcode";
+ UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelButtonPressed:)];
+ self.navigationItem.leftBarButtonItem = cancel;
+ [cancel release];
+
+ headerLabel.text = @"Enter your passcode";
+ } else if (mode == KKPasscodeModeChange) {
+ self.navigationItem.title = @"Change Passcode";
+ UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelButtonPressed:)];
+ self.navigationItem.leftBarButtonItem = cancel;
+ [cancel release];
+
+ if ([textField isEqual:_enterPasscodeTextField]) {
+ headerLabel.text = @"Enter your old passcode";
+ } else if ([textField isEqual:_setPasscodeTextField]) {
+ headerLabel.text = @"Enter your new passcode";
+ } else {
+ headerLabel.text = @"Re-enter your new passcode";
+ }
+ } else {
+ self.navigationItem.title = @"Enter Passcode";
+ headerLabel.text = @"Enter your passcode";
+ }
+
+ [headerView addSubview:headerLabel];
+ [headerLabel release];
+
+ return headerView;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (NSArray*)squares
+{
+ NSMutableArray *squareViews = [[NSMutableArray alloc] initWithCapacity:4];
+ NSInteger squareX = 23.0;
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ squareX = 133.0;
+ }
+
+ for (int i = 0; i < 4; i++) {
+ UIImageView *square = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"passcode_square_empty.png"]];
+ square.frame = CGRectMake(squareX, 74.0, 61.0, 53.0);
+ [squareViews addObject:square];
+ [square release];
+ squareX += 71.0;
+ }
+ return [[NSArray alloc] initWithArray:squareViews];
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark Table view data source
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+ return 1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+
+ static NSString *CellIdentifier = @"Cell";
+
+ UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ }
+
+ if ([aTableView isEqual:_enterPasscodeTableView]) {
+ cell.accessoryView = _enterPasscodeTextField;
+ } else if ([aTableView isEqual:_setPasscodeTableView]) {
+ cell.accessoryView = _setPasscodeTextField;
+ } else if ([aTableView isEqual:_confirmPasscodeTableView]) {
+ cell.accessoryView = _confirmPasscodeTextField;
+ }
+
+ return cell;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark UITextFieldDelegate
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (BOOL)textFieldShouldReturn:(UITextField *)textField
+{
+ if ([textField isEqual:[_textFields lastObject]]) {
+ [self doneButtonPressed];
+ } else {
+ [self nextDigitPressed];
+ }
+ return NO;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
+{
+
+ if (YES) {
+ NSString *result = [textField.text stringByReplacingCharactersInRange:range withString:string];
+
+ textField.text = result;
+
+ for (int i = 0; i < 4; i++) {
+ UIImageView *square = [[_squares objectAtIndex:_tableIndex] objectAtIndex:i];
+ if (i < [result length]) {
+ square.image = [UIImage imageNamed:@"passcode_square_filled.png"];
+ } else {
+ square.image = [UIImage imageNamed:@"passcode_square_empty.png"];
+ }
+ }
+
+ if ([result length] == 4) {
+
+ if (mode == KKPasscodeModeDisabled) {
+ NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
+ if ([_enterPasscodeTextField.text isEqualToString:passcode]) {
+ if ([KKKeychain setString:@"NO" forKey:@"passcode_on"]) {
+ [KKKeychain setString:@"" forKey:@"passcode"];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ } else {
+ [self incrementAndShowFailedAttemptsLabel];
+ }
+ } else if (mode == KKPasscodeModeEnter) {
+ NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
+ if ([_enterPasscodeTextField.text isEqualToString:passcode]) {
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ [UIView beginAnimations:@"fadeIn" context:nil];
+ [UIView setAnimationDelay:0.25];
+ [UIView setAnimationDuration:0.5];
+
+ [UIView commitAnimations];
+ }
+ if ([_delegate respondsToSelector:@selector(didPasscodeEnteredCorrectly:)]) {
+ [_delegate performSelector:@selector(didPasscodeEnteredCorrectly:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ } else {
+ [self incrementAndShowFailedAttemptsLabel];
+ }
+ } else if (mode == KKPasscodeModeChange) {
+ NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
+ if ([textField isEqual:_enterPasscodeTextField]) {
+ if ([passcode isEqualToString:_enterPasscodeTextField.text]) {
+ [self moveToNextTableView];
+ } else {
+ [self incrementAndShowFailedAttemptsLabel];
+ }
+ } else if ([textField isEqual:_setPasscodeTextField]) {
+ if ([passcode isEqualToString:_setPasscodeTextField.text]) {
+ _setPasscodeTextField.text = @"";
+ for (int i = 0; i < 4; i++) {
+ [[[_squares objectAtIndex:_tableIndex] objectAtIndex:i] setImage:[UIImage imageNamed:@"passcode_square_empty.png"]];
+ }
+ _passcodeConfirmationWarningLabel.text = @"Enter a different passcode. Cannot re-use the same passcode.";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 131.5, _viewWidth, 60.0);
+ } else {
+ _passcodeConfirmationWarningLabel.text = @"";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 146.5, _viewWidth, 30.0);
+ [self moveToNextTableView];
+ }
+ } else if ([textField isEqual:_confirmPasscodeTextField]) {
+ if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
+ _confirmPasscodeTextField.text = @"";
+ _setPasscodeTextField.text = @"";
+ _passcodeConfirmationWarningLabel.text = @"Passcodes did not match. Try again.";
+ [self moveToPreviousTableView];
+ } else {
+ if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
+ [KKKeychain setString:@"YES" forKey:@"passcode_on"];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ }
+ } else if ([textField isEqual:_setPasscodeTextField]) {
+ [self moveToNextTableView];
+ } else if ([textField isEqual:_confirmPasscodeTextField]) {
+ if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
+ _confirmPasscodeTextField.text = @"";
+ _setPasscodeTextField.text = @"";
+ _passcodeConfirmationWarningLabel.text = @"Passcodes did not match. Try again.";
+ [self moveToPreviousTableView];
+ } else {
+ if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
+ [KKKeychain setString:@"YES" forKey:@"passcode_on"];
+ }
+
+ if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
+ [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
+ }
+
+ [self dismissModalViewControllerAnimated:YES];
+ }
+ }
+ }
+
+ return NO;
+ }
+
+ return YES;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark Memory management
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)dealloc {
+
+ [_enterPasscodeTextField release];
+ [_setPasscodeTextField release];
+ [_confirmPasscodeTextField release];
+ [_tableViews release];
+ [_textFields release];
+ [_squares release];
+
+ [super dealloc];
+}
+
+@end
View
BIN  Images/passcode_square_empty.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Images/passcode_square_empty@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Images/passcode_square_filled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Images/passcode_square_filled@2x.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Please sign in to comment.
Something went wrong with that request. Please try again.