Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge remote-tracking branch 'upstream/master'

* upstream/master:
  Fixes some issues with the passcode lock
  Make "About" view work in landscape on iPad
  passcode lock support
  Minor formatting changes in README
  Minor formatting changes in README
  Converted README to Markdown, added license information.
  Added setting to disable payments download
  reviews download fix
  • Loading branch information...
commit 5a18fe28359a6bd00de2384900fcb85db77102f1 2 parents 2d63875 + fd918a9
@TheRealKerni authored
Showing with 1,639 additions and 74 deletions.
  1. +48 −0 AppSales.xcodeproj/project.pbxproj
  2. +3 −0  Classes/AboutViewController.m
  3. +6 −1 Classes/AccountsViewController.h
  4. +34 −8 Classes/AccountsViewController.m
  5. +3 −1 Classes/AppSalesAppDelegate.h
  6. +63 −6 Classes/AppSalesAppDelegate.m
  7. +4 −0 Classes/DashboardViewController.h
  8. +13 −1 Classes/DashboardViewController.m
  9. +3 −1 Classes/FieldEditorViewController.m
  10. +28 −0 Classes/KKKeychain.h
  11. +102 −0 Classes/KKKeychain.m
  12. +35 −0 Classes/KKPasscodeLock.h
  13. +59 −0 Classes/KKPasscodeLock.m
  14. +43 −0 Classes/KKPasscodeSettingsViewController.h
  15. +237 −0 Classes/KKPasscodeSettingsViewController.m
  16. +86 −0 Classes/KKPasscodeViewController.h
  17. +772 −0 Classes/KKPasscodeViewController.m
  18. +2 −1  Classes/ReportDownloadOperation.m
  19. +2 −2 Classes/ReviewDownloadManager.m
  20. +9 −0 Classes/ReviewsViewController.m
  21. +17 −9 Classes/SalesViewController.m
  22. BIN  Images/passcode_square_empty.png
  23. BIN  Images/passcode_square_empty@2x.png
  24. BIN  Images/passcode_square_filled.png
  25. BIN  Images/passcode_square_filled@2x.png
  26. +3 −0  OtherSources/GlobalSettings.h
  27. +67 −0 README.md
  28. +0 −44 README.textile
View
48 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>"; };
@@ -1383,6 +1403,7 @@
7FDF601D13DD2125005F1E2D /* Dashboard */,
7FDF602013DD21D5005F1E2D /* Common UI */,
7F7F1D0413F63F7F00F18143 /* Promo Code Download */,
+ 7F88897A14CD4C9A007604D7 /* Passcode Lock */,
7F1499D113BE312100D89064 /* Report Download */,
7F89BCDA13DFA43E001C607F /* Review Download */,
7F709D9013BD06570008DBAD /* Model */,
@@ -1487,6 +1508,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 */,
@@ -1619,6 +1644,21 @@
name = Payments;
sourceTree = "<group>";
};
+ 7F88897A14CD4C9A007604D7 /* Passcode Lock */ = {
+ 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 */,
+ );
+ name = "Passcode Lock";
+ sourceTree = "<group>";
+ };
7F89BCDA13DFA43E001C607F /* Review Download */ = {
isa = PBXGroup;
children = (
@@ -2842,6 +2882,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 +2954,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
3  Classes/AboutViewController.m
@@ -38,6 +38,9 @@ - (void)viewWillAppear:(BOOL)animated
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
+ if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
+ return YES;
+ }
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
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
42 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];
@@ -407,7 +416,8 @@ - (void)showSettings
currencySection.exclusiveSelection = YES;
FieldSpecifier *currencySectionField = [FieldSpecifier subsectionFieldWithSection:currencySection key:@"currency"];
FieldSpecifier *updateExchangeRatesButtonField = [FieldSpecifier buttonFieldWithKey:kUpdateExchangeRatesButton title:NSLocalizedString(@"Update Exchange Rates Now", nil)];
- FieldSectionSpecifier *mainSection = [FieldSectionSpecifier sectionWithFields:[NSArray arrayWithObjects:currencySectionField, updateExchangeRatesButtonField, nil]
+ FieldSpecifier *downloadPaymentsField = [FieldSpecifier switchFieldWithKey:kSettingDownloadPayments title:NSLocalizedString(@"Download Payments", nil) defaultValue:[[NSUserDefaults standardUserDefaults] boolForKey:kSettingDownloadPayments]];
+ 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)];
@@ -441,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;
}
@@ -515,14 +525,13 @@ - (void)fieldEditor:(FieldEditorViewController *)editor didFinishEditingWithValu
[[CurrencyManager sharedManager] setBaseCurrency:[[key componentsSeparatedByString:@"."] lastObject]];
}
}
-
- if ([key hasPrefix:@"sortby."]) {
+ if ([key hasPrefix:@"sortby."]) {
if ([[returnValues objectForKey:key] boolValue]) {
- [[NSUserDefaults standardUserDefaults] setObject:[[key componentsSeparatedByString:@"."] lastObject] forKey:@"ProductSortby"];
+ [[NSUserDefaults standardUserDefaults] setObject:[[key componentsSeparatedByString:@"."] lastObject] forKey:@"ProductSortby"];
}
}
-
}
+ [[NSUserDefaults standardUserDefaults] setBool:[[returnValues objectForKey:kSettingDownloadPayments] boolValue] forKey:kSettingDownloadPayments];
[self dismissModalViewControllerAnimated:YES];
[[NSNotificationCenter defaultCenter] postNotificationName:ASViewSettingsDidChangeNotification object:nil];
@@ -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
4 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;
@@ -37,5 +38,6 @@
- (NSURL *)applicationSupportDirectory;
- (void)loadAccount:(ASAccount *)account;
- (void)selectAccount:(id)sender;
+- (void)showPasscodeLockIfNeeded;
@end
View
69 Classes/AppSalesAppDelegate.m
@@ -26,13 +26,20 @@ @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];
+ NSDictionary *defaults = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES], kSettingDownloadPayments,
+ nil];
+ [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(promoCodeLicenseAgreementLoaded:) name:@"PromoCodeOperationLoadedLicenseAgreementNotification" object:nil];
-
- BOOL iPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
+ BOOL iPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
if (!iPad) {
AccountsViewController *rootViewController = [[[AccountsViewController alloc] initWithStyle:UITableViewStyleGrouped] autorelease];
rootViewController.managedObjectContext = self.managedObjectContext;
@@ -42,7 +49,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
- } else {
+ } else {
self.accountsViewController = [[[AccountsViewController alloc] initWithStyle:UITableViewStyleGrouped] autorelease];
self.accountsViewController.managedObjectContext = self.managedObjectContext;
self.accountsViewController.contentSizeForViewInPopover = CGSizeMake(320, 480);
@@ -52,7 +59,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
self.accountsPopover = [[[UIPopoverController alloc] initWithContentViewController:accountsNavController] autorelease];
[self loadAccount:nil];
[self.window makeKeyAndVisible];
- [self selectAccount:nil];
}
BOOL migrating = [self migrateDataIfNeeded];
@@ -74,12 +80,19 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
[self.accountsViewController performSelector:@selector(downloadReports:) withObject:nil afterDelay:0.0];
}
+ [self showPasscodeLockIfNeeded];
+ if (iPad) {
+ [self selectAccount:nil];
+ }
+
return YES;
}
- (void)selectAccount:(id)sender
{
- [self.accountsPopover presentPopoverFromRect:CGRectMake(50, 50, 1, 1) inView:self.window.rootViewController.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
+ if (!self.window.rootViewController.modalViewController) {
+ [self.accountsPopover presentPopoverFromRect:CGRectMake(50, 50, 1, 1) inView:self.window.rootViewController.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
+ }
}
- (void)accountsViewController:(AccountsViewController *)viewController didSelectAccount:(ASAccount *)account
@@ -128,7 +141,7 @@ - (void)loadAccount:(ASAccount *)account
UITabBarController *tabController = [[[UITabBarController alloc] initWithNibName:nil bundle:nil] autorelease];
[tabController setViewControllers:[NSArray arrayWithObjects:salesNavController, reviewsNavController, paymentsNavController, promoNavController, nil]];
- self.window.rootViewController = tabController;
+ self.window.rootViewController = tabController;
}
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
@@ -203,6 +216,50 @@ - (void)applicationDidBecomeActive:(UIApplication *)application
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}
+- (void)applicationWillResignActive:(UIApplication *)application
+{
+ [self showPasscodeLockIfNeeded];
+}
+
+- (void)showPasscodeLockIfNeeded
+{
+ if ([[KKPasscodeLock sharedLock] isPasscodeRequired]) {
+
+ KKPasscodeViewController *vc = [[[KKPasscodeViewController alloc] initWithNibName:nil bundle:nil] autorelease];
+ vc.mode = KKPasscodeModeEnter;
+ vc.delegate = self;
+
+ UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
+ if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+ nav.modalPresentationStyle = UIModalPresentationFullScreen;
+ 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;
+ }
+ UIViewController *viewControllerForPresentingPasscode = nil;
+ if (self.window.rootViewController.modalViewController) {
+ if ([self.window.rootViewController.modalViewController isKindOfClass:[UINavigationController class]]
+ && [[[(UINavigationController *)self.window.rootViewController.modalViewController viewControllers] objectAtIndex:0] isKindOfClass:[KKPasscodeViewController class]]) {
+ //The passcode dialog is already shown...
+ return;
+ }
+ //We're in the settings or add account dialog...
+ viewControllerForPresentingPasscode = self.window.rootViewController.modalViewController;
+ } else {
+ viewControllerForPresentingPasscode = self.window.rootViewController;
+ }
+ if (self.accountsPopover.popoverVisible) {
+ [self.accountsPopover dismissPopoverAnimated:NO];
+ }
+ [[NSNotificationCenter defaultCenter] postNotificationName:ASWillShowPasscodeLockNotification object:self];
+ [viewControllerForPresentingPasscode presentModalViewController:nav animated:NO];
+ }
+}
+
- (void)applicationWillTerminate:(UIApplication *)application
{
[self saveContext];
View
4 Classes/DashboardViewController.h
@@ -30,6 +30,8 @@
UIActivityIndicatorView *activityIndicator;
UILabel *statusLabel;
UIProgressView *progressBar;
+
+ UIActionSheet *activeSheet;
}
@property (nonatomic, retain) ASAccount *account;
@@ -45,8 +47,10 @@
@property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;
@property (nonatomic, retain) UILabel *statusLabel;
@property (nonatomic, retain) UIProgressView *progressBar;
+@property (nonatomic, retain) UIActionSheet *activeSheet;
- (id)initWithAccount:(ASAccount *)anAccount;
+- (void)willShowPasscodeLock:(NSNotification *)notification;
- (NSSet *)entityNamesTriggeringReload;
- (void)reloadData;
- (void)reloadTableView;
View
14 Classes/DashboardViewController.m
@@ -17,7 +17,7 @@ @implementation DashboardViewController
@synthesize account, products, visibleProducts, selectedProduct;
@synthesize productsTableView, topView, shadowView, colorPopover, statusToolbar, stopButtonItem, activityIndicator, statusLabel, progressBar;
-
+@synthesize activeSheet;
- (id)initWithAccount:(ASAccount *)anAccount
{
@@ -26,10 +26,21 @@ - (id)initWithAccount:(ASAccount *)anAccount
self.account = anAccount;
self.hidesBottomBarWhenPushed = [[UIDevice currentDevice] userInterfaceIdiom] != UIUserInterfaceIdiomPad;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:[account managedObjectContext]];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willShowPasscodeLock:) name:ASWillShowPasscodeLockNotification object:nil];
}
return self;
}
+- (void)willShowPasscodeLock:(NSNotification *)notification
+{
+ if (self.colorPopover.popoverVisible) {
+ [self.colorPopover dismissPopoverAnimated:NO];
+ }
+ if (self.activeSheet.visible) {
+ [self.activeSheet dismissWithClickedButtonIndex:self.activeSheet.cancelButtonIndex animated:NO];
+ }
+}
+
- (void)contextDidChange:(NSNotification *)notification
{
NSSet *relevantEntityNames = [self entityNamesTriggeringReload];
@@ -362,6 +373,7 @@ - (void)dealloc
[statusLabel release];
[progressBar release];
[colorPopover release];
+ [activeSheet release];
[super dealloc];
}
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
28 Classes/KKKeychain.h
@@ -0,0 +1,28 @@
+//
+// 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
102 Classes/KKKeychain.m
@@ -0,0 +1,102 @@
+//
+// 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
59 Classes/KKPasscodeLock.m
@@ -0,0 +1,59 @@
+//
+// 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
237 Classes/KKPasscodeSettingsViewController.m
@@ -0,0 +1,237 @@
+//
+// 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
86 Classes/KKPasscodeViewController.h
@@ -0,0 +1,86 @@
+//
+// 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;
+}
+
+@property (nonatomic, assign) id <KKPasscodeViewControllerDelegate> delegate;
+@property (nonatomic, assign) KKPasscodeMode mode;
+
+@property (nonatomic, retain) UITableView *enterPasscodeTableView;
+@property (nonatomic, retain) UITableView *setPasscodeTableView;
+@property (nonatomic, retain) UITableView *confirmPasscodeTableView;
+
+
+@end
+
View
772 Classes/KKPasscodeViewController.m
@@ -0,0 +1,772 @@
+//
+// 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*)newPasscodeTextField;
+- (NSArray*)squares;
+- (UIView*)passwordHeaderViewForTextField:(UITextField*)textField;
+- (void)moveToNextTableView;
+- (void)moveToPreviousTableView;
+
+@end
+
+
+@implementation KKPasscodeViewController
+
+@synthesize delegate = _delegate;
+@synthesize mode;
+@synthesize enterPasscodeTableView=_enterPasscodeTableView, setPasscodeTableView=_setPasscodeTableView, confirmPasscodeTableView=_confirmPasscodeTableView;
+
+#pragma mark -
+#pragma mark UIViewController
+
+- (void)loadView
+{
+ [super loadView];
+
+ self.view.backgroundColor = [UIColor whiteColor];
+
+ self.enterPasscodeTableView = [[[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped] autorelease];
+ _enterPasscodeTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ _enterPasscodeTableView.delegate = self;
+ _enterPasscodeTableView.dataSource = self;
+ _enterPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ _enterPasscodeTableView.backgroundColor = [UIColor groupTableViewBackgroundColor];
+ [self.view addSubview:_enterPasscodeTableView];
+
+ self.setPasscodeTableView = [[[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped] autorelease];
+ _setPasscodeTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ _setPasscodeTableView.delegate = self;
+ _setPasscodeTableView.dataSource = self;
+ _setPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ _setPasscodeTableView.backgroundColor = [UIColor groupTableViewBackgroundColor];
+ [self.view addSubview:_setPasscodeTableView];
+
+ self.confirmPasscodeTableView = [[[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped] autorelease];
+ _confirmPasscodeTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ _confirmPasscodeTableView.delegate = self;
+ _confirmPasscodeTableView.dataSource = self;
+ _confirmPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ _confirmPasscodeTableView.backgroundColor = [UIColor groupTableViewBackgroundColor];
+ [self.view addSubview:_confirmPasscodeTableView];
+
+ [self.view addGestureRecognizer:[[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(viewTapped:)] autorelease]];
+}
+
+- (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"];
+
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ _enterPasscodeTextField = [self newPasscodeTextField];
+ _setPasscodeTextField = [self newPasscodeTextField];
+ _confirmPasscodeTextField = [self newPasscodeTextField];
+
+ _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]];
+ UIView *squaresView = [[[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width * 0.5 - 71.0 * 4 * 0.5, 0, 71.0 * 4, 53)] autorelease];
+ squaresView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
+ for (int i = 0; i < [[_squares lastObject] count]; i++) {
+ [squaresView addSubview:[[_squares lastObject] objectAtIndex:i]];
+ }
+ [_enterPasscodeTableView.tableHeaderView addSubview:squaresView];
+ }
+
+ _setPasscodeTableView.tableHeaderView = [self passwordHeaderViewForTextField:_setPasscodeTextField];
+ [_tableViews addObject:_setPasscodeTableView];
+ [_textFields addObject:_setPasscodeTextField];
+ [_squares addObject:[self squares]];
+ UIView *squaresView = [[[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width * 0.5 - 71.0 * 4 * 0.5, 0, 71.0 * 4, 53)] autorelease];
+ squaresView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
+ for (int i = 0; i < [[_squares lastObject] count]; i++) {
+ [squaresView addSubview:[[_squares lastObject] objectAtIndex:i]];
+ }
+ [_setPasscodeTableView.tableHeaderView addSubview:squaresView];
+
+ _confirmPasscodeTableView.tableHeaderView = [self passwordHeaderViewForTextField:_confirmPasscodeTextField];
+ [_tableViews addObject:_confirmPasscodeTableView];
+ [_textFields addObject:_confirmPasscodeTextField];
+ [_squares addObject:[self squares]];
+ UIView *squaresConfirmView = [[[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width * 0.5 - 71.0 * 4 * 0.5, 0, 71.0 * 4, 53)] autorelease];
+ squaresConfirmView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
+ for (int i = 0; i < [[_squares lastObject] count]; i++) {
+ [squaresConfirmView addSubview:[[_squares lastObject] objectAtIndex:i]];
+ }
+ [_confirmPasscodeTableView.tableHeaderView addSubview:squaresConfirmView];
+ } else {
+ _enterPasscodeTableView.tableHeaderView = [self passwordHeaderViewForTextField:_enterPasscodeTextField];
+ [_tableViews addObject:_enterPasscodeTableView];
+ [_textFields addObject:_enterPasscodeTextField];
+ [_squares addObject:[self squares]];
+ UIView *squaresView = [[[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width * 0.5 - 71.0 * 4 * 0.5, 0, 71.0 * 4, 53)] autorelease];
+ squaresView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
+ for (int i = 0; i < [[_squares lastObject] count]; i++) {
+ [squaresView addSubview:[[_squares lastObject] objectAtIndex:i]];
+ }
+ [_enterPasscodeTableView.tableHeaderView addSubview:squaresView];
+ }
+
+ [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 + self.view.bounds.size.width, 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, self.view.bounds.size.width, self.view.bounds.size.height);
+ }
+ }
+}
+
+- (void)viewTapped:(UITapGestureRecognizer *)recognizer
+{
+ [[_textFields objectAtIndex:_tableIndex] becomeFirstResponder];
+}
+
+#pragma mark -
+#pragma mark Private
+
+- (UITextField*)newPasscodeTextField
+{
+ 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((self.view.bounds.size.width - (size.width + 36.0)) / 2, 147.5, size.width + 36.0, size.height + 10.0);
+ _failedAttemptsLabel.frame = CGRectMake((self.view.bounds.size.width - (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 + self.view.bounds.size.width, 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 - self.view.bounds.size.width, 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 - self.view.bounds.size.width, 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 + self.view.bounds.size.width, 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, self.view.bounds.size.width, 60.0);
+ } else {
+ _passcodeConfirmationWarningLabel.text = @"";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 146.5, self.view.bounds.size.width, 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, self.view.bounds.size.width, 60.0);
+ } else {
+ _passcodeConfirmationWarningLabel.text = @"";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 146.5, self.view.bounds.size.width, 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, self.view.bounds.size.width, 70.0)] autorelease];
+ UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 27.5, self.view.bounds.size.width, 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, self.view.bounds.size.width, 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((self.view.bounds.size.width - (size.width + 36.0)) / 2, 147.5, size.width + 36.0, size.height + 10.0)];
+ _failedAttemptsLabel = [[UILabel alloc] initWithFrame:CGRectMake((self.view.bounds.size.width - (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";
+ }
+ headerLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
+
+ [headerView addSubview:headerLabel];
+ [headerLabel release];
+
+ return headerView;
+}
+
+- (NSArray*)squares
+{
+ NSMutableArray *squareViews = [NSMutableArray array];
+
+ CGFloat squareX = 0.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 arrayWithArray: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, self.view.bounds.size.width, 60.0);
+ } else {
+ _passcodeConfirmationWarningLabel.text = @"";
+ _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 146.5, self.view.bounds.size.width, 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
3  Classes/ReportDownloadOperation.m
@@ -212,7 +212,8 @@ - (void)main
return;
}
- if (numberOfReportsDownloaded > 0 || [account.payments count] == 0) {
+ BOOL downloadPayments = [[NSUserDefaults standardUserDefaults] boolForKey:kSettingDownloadPayments];
+ if (downloadPayments && (numberOfReportsDownloaded > 0 || [account.payments count] == 0)) {
//==== Payments
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [cookieStorage cookiesForURL:[NSURL URLWithString:@"https://itunesconnect.apple.com"]];
View
4 Classes/ReviewDownloadManager.m
@@ -324,8 +324,8 @@ - (NSArray *)reviewInfosFromHTML:(NSString *)html
}
NSString *reviewText = nil;
- [scanner scanUpToString:@"<p class=\"content more-text\" truncate-style=\"paragraph\" truncate-length=\"5\">" intoString:NULL];
- [scanner scanString:@"<p class=\"content more-text\" truncate-style=\"paragraph\" truncate-length=\"5\">" intoString:NULL];
+ [scanner scanUpToString:@"<p class=\"content\" will-truncate-max-height=\"0\" data-text-truncate-lines=\"5\">" intoString:NULL];
+ [scanner scanString:@"<p class=\"content\" will-truncate-max-height=\"0\" data-text-truncate-lines=\"5\">" intoString:NULL];
[scanner scanUpToString:@"</p>" intoString:&reviewText];
reviewText = [reviewText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
View
9 Classes/ReviewsViewController.m
@@ -22,10 +22,19 @@ - (id)initWithAccount:(ASAccount *)anAccount
self.title = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) ? NSLocalizedString(@"Reviews", nil) : [account displayName];
self.tabBarItem.image = [UIImage imageNamed:@"Reviews.png"];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reviewDownloadProgressDidChange:) name:ReviewDownloadManagerDidUpdateProgressNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willShowPasscodeLock:) name:ASWillShowPasscodeLockNotification object:nil];
}
return self;
}
+- (void)willShowPasscodeLock:(NSNotification *)notification
+{
+ [super willShowPasscodeLock:notification];
+ if (self.reviewsPopover.popoverVisible) {
+ [self.reviewsPopover dismissPopoverAnimated:NO];
+ }
+}
+
- (void)loadView
{
[super loadView];
View
26 Classes/SalesViewController.m
@@ -61,10 +61,19 @@ - (id)initWithAccount:(ASAccount *)anAccount
showWeeks = [[NSUserDefaults standardUserDefaults] boolForKey:kSettingDashboardShowWeeks];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadData) name:ASViewSettingsDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willShowPasscodeLock:) name:ASWillShowPasscodeLockNotification object:nil];
}
return self;
}
+- (void)willShowPasscodeLock:(NSNotification *)notification
+{
+ [super willShowPasscodeLock:notification];
+ if (self.selectedReportPopover.popoverVisible) {
+ [self.selectedReportPopover dismissPopoverAnimated:NO];
+ }
+}
+
- (void)loadView
{
[super loadView];
@@ -324,23 +333,22 @@ - (void)switchTab:(UISegmentedControl *)modeControl
- (void)showGraphOptions:(id)sender
{
- UIActionSheet *sheet = nil;
if (selectedTab == 0) {
- sheet = [[[UIActionSheet alloc] initWithTitle:nil
+ self.activeSheet = [[[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:NSLocalizedString(@"Cancel", nil)
destructiveButtonTitle:nil
otherButtonTitles:NSLocalizedString(@"Daily Reports", nil), NSLocalizedString(@"Weekly Reports", nil), nil] autorelease];
- sheet.tag = kSheetTagDailyGraphOptions;
+ self.activeSheet.tag = kSheetTagDailyGraphOptions;
} else {
- sheet = [[[UIActionSheet alloc] initWithTitle:nil
+ self.activeSheet = [[[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:NSLocalizedString(@"Cancel", nil)
destructiveButtonTitle:nil
otherButtonTitles:NSLocalizedString(@"Calendar Months", nil), NSLocalizedString(@"Fiscal Months", nil), nil] autorelease];
- sheet.tag = kSheetTagMonthlyGraphOptions;
+ self.activeSheet.tag = kSheetTagMonthlyGraphOptions;
}
- [sheet showInView:self.view];
+ [self.activeSheet showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
@@ -710,7 +718,7 @@ - (UIView *)accessoryViewForRowAtIndexPath:(NSIndexPath *)indexPath
- (void)selectAdvancedViewMode:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
- UIActionSheet *sheet = [[[UIActionSheet alloc] initWithTitle:nil
+ self.activeSheet = [[[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:NSLocalizedString(@"Cancel", nil)
destructiveButtonTitle:nil
@@ -721,8 +729,8 @@ - (void)selectAdvancedViewMode:(UILongPressGestureRecognizer *)gestureRecognizer
NSLocalizedString(@"Educational Sales", nil),
NSLocalizedString(@"Gift Purchases", nil),
NSLocalizedString(@"Promo Codes", nil), nil] autorelease];
- sheet.tag = kSheetTagAdvancedViewMode;
- [sheet showInView:self.navigationController.view];
+ self.activeSheet.tag = kSheetTagAdvancedViewMode;
+ [self.activeSheet showInView:self.navigationController.view];
}
}
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.
View
3  OtherSources/GlobalSettings.h
@@ -11,3 +11,6 @@
#define kSettingDashboardViewMode @"DashboardViewMode"
#define kSettingShow14Days @"DashboardShow14Days"
#define kSettingDashboardShowWeeks @"DashboardShowWeeks"
+#define kSettingDownloadPayments @"DownloadPayments"
+
+#define ASWillShowPasscodeLockNotification @"ASWillShowPasscodeLockNotification"
View
67 README.md
@@ -0,0 +1,67 @@
+#AppSales
+
+AppSales allows iOS and Mac App Store developers to download and analyze their sales reports from iTunes Connect on the iPhone.
+
+##Features
+* Automatic download of daily and weekly sales reports from iTunes Connect
+* Stacked bar graphs to see all your sales at a glance
+* Show your sales on a world map
+* View actual payments by Apple on a calendar
+* Group daily reports by fiscal or calendar month to predict your next payment
+* Automatic conversion to your currency of choice
+* Download customer reviews of your apps
+* Import reports that you downloaded elsewhere (for example with [AppViz](http://www.ideaswarm.com) on your Mac) via iTunes File Sharing
+* Optional push notifications when new reports are available via Boxcar
+
+Please see the screenshot below for a visual guide to AppSales' main interface and some tips and tricks.
+
+##Requirements
+AppSales requires the iOS 4.2 SDK or later.
+
+Because there is no API to access some parts of iTunes Connect, AppSales scrapes [itunesconnect.apple.com](https://itunesconnect.apple.com). This means that even small changes on this website can break some functionality. In most cases, this is easy to fix and I'll try to make a new version available here. The report download itself uses Apple's auto-ingestion interface to iTunes Connect and should generally be unaffected by changes to the website.
+
+##Push Notifications
+Because sales reports are not always available at the same time, I run a web service on [Google App Engine](http://code.google.com/appengine/) to check for new reports every 10 minutes (until the report for the previous day is available).
+
+You can get a push notification when new reports are available with the free [Boxcar](http://itunes.apple.com/us/app/boxcar/id321493542) app. You can install Boxcar and add the AppSales service from the settings (in AppSales).
+
+If AppSales is installed, opening the push notification will automatically trigger the download of new reports but you can also use the push service without AppSales being installed. To do so, install [Boxcar](http://itunes.apple.com/us/app/boxcar/id321493542) and open this link from Safari (on your device) to add the AppSales service: [boxcar://provider/965](boxcar://provider/965).
+
+Except for the total number of subscribers, I don't collect any data with the push service. You can stop receiving notifications at any time by simply removing AppSales from Boxcar.
+
+##About
+You can follow me on Twitter for updates on the development: [@olemoritz](http://twitter.com/olemoritz)
+
+If you'd like to support this effort, please consider a donation via PayPal or Flattr:
+
+<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=YDQN4S3WVRCBU&lc=US&item_name=AppSales&no_note=1&currency_code=USD"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif"/></a> <a href="http://flattr.com/thing/366574/AppSales" target="_blank">
+<img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a>
+
+![AppSales Screenshot](http://github.com/omz/AppSales-Mobile/raw/master/Screenshot.png)
+
+##License
+In addition to the BSD-2 license below, I ask that you do not publicly distribute the app as a whole in binary form (e.g. on the App Store).
+
+ Copyright (c) 2011, Ole Zorn
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
View
44 README.textile
@@ -1,44 +0,0 @@
-h1. AppSales
-
-AppSales allows iOS and Mac App Store developers to download and analyze their sales reports from iTunes Connect on the iPhone.
-
-h2. Features
-
-* Automatic download of daily and weekly sales reports from iTunes Connect
-* Stacked bar graphs to see all your sales at a glance
-* Show your sales on a world map
-* View actual payments by Apple on a calendar
-* Group daily reports by fiscal or calendar month to predict your next payment
-* Automatic conversion to your currency of choice
-* Download customer reviews of your apps
-* Import reports that you downloaded elsewhere (for example with "AppViz":http://www.ideaswarm.com on your Mac) via iTunes File Sharing
-* Optional push notifications when new reports are available via Boxcar
-
-Please see the screenshot below for a visual guide to AppSales' main interface and some tips and tricks.
-
-h2. Requirements
-
-AppSales requires the iOS 4.2 SDK or later.
-
-Because there is no API to access some parts of iTunes Connect, AppSales scrapes "itunesconnect.apple.com":https://itunesconnect.apple.com. This means that even small changes on this website can break some functionality. In most cases, this is easy to fix and I'll try to make a new version available here. The report download itself uses Apple's auto-ingestion interface to iTunes Connect and should generally be unaffected by changes to the website.
-
-h2. Push Notifications
-
-Because sales reports are not always available at the same time, I run a web service on "Google App Engine":http://code.google.com/appengine/ to check for new reports every 15 minutes (until the report for the previous day is available).
-
-You can get a push notification when new reports are available with the free "Boxcar":http://itunes.apple.com/us/app/boxcar/id321493542 app. You can install Boxcar and add the AppSales service from the settings (in AppSales).
-
-If AppSales is installed, opening the push notification will automatically trigger the download of new reports but you can also use the push service without AppSales being installed. To do so, install "Boxcar":http://itunes.apple.com/us/app/boxcar/id321493542 and open this link from Safari to add the AppSales service: "boxcar://provider/965":boxcar://provider/965.
-
-Except for the total number of subscribers, I don't collect any data with the push service. You can stop receiving notifications at any time by simply removing AppSales from Boxcar.
-
-h2. About
-
-You can follow me on Twitter for updates on the development: "@olemoritz":http://twitter.com/olemoritz
-
-If you'd like to support this effort, please consider a donation via PayPal or Flattr:
-
-<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=YDQN4S3WVRCBU&lc=US&item_name=AppSales&no_note=1&currency_code=USD"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif"/></a> <a href="http://flattr.com/thing/366574/AppSales" target="_blank">
-<img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a>
-
-!http://github.com/omz/AppSales-Mobile/raw/master/Screenshot.png(AppSales screenshot)!
Please sign in to comment.
Something went wrong with that request. Please try again.