KKPasscodeViewController.m 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  1. //
  2. // Copyright 2011-2012 Kosher Penguin LLC
  3. // Created by Adar Porat (https://github.com/aporat) on 1/16/2012.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. #import "KKPasscodeViewController.h"
  18. #import "KKKeychain.h"
  19. #import "KKPasscodeSettingsViewController.h"
  20. #import "KKPasscodeLock.h"
  21. #import "BundleUtil.h"
  22. #import "AppGroup.h"
  23. #import <QuartzCore/QuartzCore.h>
  24. #import <AudioToolbox/AudioToolbox.h>
  25. #import "ThreemaFramework/ThreemaFramework-swift.h"
  26. #define kkIsOnIOS7OrLater() ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)
  27. @interface KKPasscodeViewController (Private)
  28. - (UITextField*)passcodeTextField;
  29. - (NSArray*)boxes;
  30. - (UIView*)headerViewForTextField:(UITextField*)textField;
  31. - (void)moveToNextTableView;
  32. - (void)moveToPreviousTableView;
  33. - (void)incrementFailedAttemptsLabel;
  34. @end
  35. @implementation KKPasscodeViewController
  36. @synthesize delegate = _delegate;
  37. @synthesize mode = _mode;
  38. #pragma mark -
  39. #pragma mark UIViewController
  40. - (void)loadView
  41. {
  42. [super loadView];
  43. if (kkIsOnIOS7OrLater())
  44. self.automaticallyAdjustsScrollViewInsets = NO;
  45. _enterPasscodeTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
  46. if (kkIsOnIOS7OrLater())
  47. _enterPasscodeTableView.contentInset = UIEdgeInsetsMake([self iOS7TableTopInset],
  48. _enterPasscodeTableView.contentInset.left,
  49. _enterPasscodeTableView.contentInset.bottom,
  50. _enterPasscodeTableView.contentInset.right);
  51. _enterPasscodeTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  52. _enterPasscodeTableView.delegate = self;
  53. _enterPasscodeTableView.dataSource = self;
  54. _enterPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  55. _enterPasscodeTableView.scrollEnabled = NO;
  56. _enterPasscodeTableView.backgroundColor = [UIColor clearColor];
  57. [self.view addSubview:_enterPasscodeTableView];
  58. _setPasscodeTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
  59. if (kkIsOnIOS7OrLater())
  60. _setPasscodeTableView.contentInset = UIEdgeInsetsMake([self iOS7TableTopInset],
  61. _enterPasscodeTableView.contentInset.left,
  62. _enterPasscodeTableView.contentInset.bottom,
  63. _enterPasscodeTableView.contentInset.right);
  64. _setPasscodeTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  65. _setPasscodeTableView.delegate = self;
  66. _setPasscodeTableView.dataSource = self;
  67. _setPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  68. _setPasscodeTableView.scrollEnabled = NO;
  69. _setPasscodeTableView.backgroundColor = [UIColor clearColor];
  70. [self.view addSubview:_setPasscodeTableView];
  71. _confirmPasscodeTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
  72. if (kkIsOnIOS7OrLater())
  73. _confirmPasscodeTableView.contentInset = UIEdgeInsetsMake([self iOS7TableTopInset],
  74. _enterPasscodeTableView.contentInset.left,
  75. _enterPasscodeTableView.contentInset.bottom,
  76. _enterPasscodeTableView.contentInset.right);
  77. _confirmPasscodeTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  78. _confirmPasscodeTableView.delegate = self;
  79. _confirmPasscodeTableView.dataSource = self;
  80. _confirmPasscodeTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  81. _confirmPasscodeTableView.scrollEnabled = NO;
  82. _confirmPasscodeTableView.backgroundColor = [UIColor clearColor];
  83. [self.view addSubview:_confirmPasscodeTableView];
  84. _shouldReleaseFirstResponser = NO;
  85. _failedAttemptsCount = [[AppGroup userDefaults] integerForKey:@"FailedCodeAttempts"];
  86. if ([Colors areCustomized]) {
  87. [self.view setBackgroundColor:[Colors background]];
  88. } else {
  89. [self.view setBackgroundColor:[Colors backgroundDark]];
  90. }
  91. [Colors updateNavigationBar:self.navigationController.navigationBar];
  92. }
  93. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
  94. return YES;
  95. }
  96. - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  97. return UIInterfaceOrientationMaskAll;
  98. }
  99. - (void)viewWillAppear:(BOOL)animated
  100. {
  101. [super viewWillAppear:animated];
  102. _passcodeLockOn = [[KKKeychain getStringForKey:@"passcode_on"] isEqualToString:@"YES"];
  103. _eraseData = [[KKPasscodeLock sharedLock] eraseOption] && [[KKKeychain getStringForKey:@"erase_data_on"] isEqualToString:@"YES"];
  104. _enterPasscodeTextField = [[UITextField alloc] init];
  105. _enterPasscodeTextField.delegate = self;
  106. _enterPasscodeTextField.keyboardType = UIKeyboardTypeNumberPad;
  107. _enterPasscodeTextField.secureTextEntry = YES;
  108. _enterPasscodeTextField.hidden = YES;
  109. _setPasscodeTextField = [[UITextField alloc] init];
  110. _setPasscodeTextField.delegate = self;
  111. _setPasscodeTextField.keyboardType = UIKeyboardTypeNumberPad;
  112. _setPasscodeTextField.secureTextEntry = YES;
  113. _setPasscodeTextField.hidden = YES;
  114. _confirmPasscodeTextField = [[UITextField alloc] init];
  115. _confirmPasscodeTextField.delegate = self;
  116. _confirmPasscodeTextField.keyboardType = UIKeyboardTypeNumberPad;
  117. _confirmPasscodeTextField.secureTextEntry = YES;
  118. _confirmPasscodeTextField.hidden = YES;
  119. _tableViews = [[NSMutableArray alloc] init];
  120. _textFields = [[NSMutableArray alloc] init];
  121. _boxes = [[NSMutableArray alloc] init];
  122. if (_mode == KKPasscodeModeSet) {
  123. self.navigationItem.title = KKPasscodeLockLocalizedString(@"Set Passcode", @"");
  124. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
  125. target:self
  126. action:@selector(cancelButtonPressed:)];
  127. } else if (_mode == KKPasscodeModeChange) {
  128. self.navigationItem.title = KKPasscodeLockLocalizedString(@"Change Passcode", @"");
  129. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
  130. target:self
  131. action:@selector(cancelButtonPressed:)];
  132. } else if (_mode == KKPasscodeModeDisabled) {
  133. self.navigationItem.title = KKPasscodeLockLocalizedString(@"Turn off Passcode", @"");
  134. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
  135. target:self
  136. action:@selector(cancelButtonPressed:)];
  137. } else {
  138. self.navigationItem.title = KKPasscodeLockLocalizedString(@"Enter Passcode", @"");
  139. }
  140. if (_mode == KKPasscodeModeSet || _mode == KKPasscodeModeChange) {
  141. if (_passcodeLockOn) {
  142. _enterPasscodeTableView.tableHeaderView = [self headerViewForTextField:_enterPasscodeTextField];
  143. [_tableViews addObject:_enterPasscodeTableView];
  144. [_textFields addObject:_enterPasscodeTextField];
  145. [_boxes addObject:[self boxes]];
  146. UIView *boxesView = [[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width * 0.5 - 71.0 * kPasscodeBoxesCount * 0.5, 74.0, 71.0 * kPasscodeBoxesCount, kPasscodeBoxHeight)];
  147. boxesView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  148. for (int i = 0; i < [[_boxes lastObject] count]; i++) {
  149. [boxesView addSubview:[[_boxes lastObject] objectAtIndex:i]];
  150. }
  151. [_enterPasscodeTableView.tableHeaderView addSubview:boxesView];
  152. }
  153. _setPasscodeTableView.tableHeaderView = [self headerViewForTextField:_setPasscodeTextField];
  154. [_tableViews addObject:_setPasscodeTableView];
  155. [_textFields addObject:_setPasscodeTextField];
  156. [_boxes addObject:[self boxes]];
  157. UIView *boxesView = [[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width * 0.5 - 71.0 * kPasscodeBoxesCount * 0.5, 74.0, 71.0 * kPasscodeBoxesCount, kPasscodeBoxHeight)];
  158. boxesView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  159. for (int i = 0; i < [[_boxes lastObject] count]; i++) {
  160. [boxesView addSubview:[[_boxes lastObject] objectAtIndex:i]];
  161. }
  162. [_setPasscodeTableView.tableHeaderView addSubview:boxesView];
  163. _confirmPasscodeTableView.tableHeaderView = [self headerViewForTextField:_confirmPasscodeTextField];
  164. [_tableViews addObject:_confirmPasscodeTableView];
  165. [_textFields addObject:_confirmPasscodeTextField];
  166. [_boxes addObject:[self boxes]];
  167. UIView *boxesConfirmView = [[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width * 0.5 - 71.0 * kPasscodeBoxesCount * 0.5, 74.0, 71.0 * kPasscodeBoxesCount, kPasscodeBoxHeight)];
  168. boxesConfirmView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  169. for (int i = 0; i < [[_boxes lastObject] count]; i++) {
  170. [boxesConfirmView addSubview:[[_boxes lastObject] objectAtIndex:i]];
  171. }
  172. [_confirmPasscodeTableView.tableHeaderView addSubview:boxesConfirmView];
  173. } else {
  174. _enterPasscodeTableView.tableHeaderView = [self headerViewForTextField:_enterPasscodeTextField];
  175. [_tableViews addObject:_enterPasscodeTableView];
  176. [_textFields addObject:_enterPasscodeTextField];
  177. [_boxes addObject:[self boxes]];
  178. UIView *boxesView = [[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width * 0.5 - 71.0 * kPasscodeBoxesCount * 0.5, 74.0, 71.0 * kPasscodeBoxesCount, kPasscodeBoxHeight)];
  179. boxesView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  180. for (int i = 0; i < [[_boxes lastObject] count]; i++) {
  181. [boxesView addSubview:[[_boxes lastObject] objectAtIndex:i]];
  182. }
  183. [_enterPasscodeTableView.tableHeaderView addSubview:boxesView];
  184. }
  185. [self.view addSubview:[_tableViews objectAtIndex:0]];
  186. for (int i = 1; i < [_tableViews count]; i++) {
  187. UITableView *tableView = [_tableViews objectAtIndex:i];
  188. tableView.frame = CGRectMake(tableView.frame.origin.x + self.view.bounds.size.width,
  189. tableView.frame.origin.y,
  190. tableView.frame.size.width,
  191. tableView.frame.size.height);
  192. if (kkIsOnIOS7OrLater())
  193. tableView.contentInset = UIEdgeInsetsMake([self iOS7TableTopInset],
  194. tableView.contentInset.left,
  195. tableView.contentInset.bottom,
  196. tableView.contentInset.right);
  197. [self.view addSubview:tableView];
  198. }
  199. [[_textFields objectAtIndex:0] becomeFirstResponder];
  200. [[_tableViews objectAtIndex:0] reloadData];
  201. [[_textFields objectAtIndex:[_tableViews count] - 1] setReturnKeyType:UIReturnKeyDone];
  202. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  203. if ([_tableViews count] > 1) {
  204. [self moveToNextTableView];
  205. [self moveToPreviousTableView];
  206. } else {
  207. UITableView *tableView = [_tableViews objectAtIndex:0];
  208. tableView.frame = CGRectMake(tableView.frame.origin.x,
  209. tableView.frame.origin.y,
  210. self.view.bounds.size.width,
  211. self.view.bounds.size.height);
  212. }
  213. }
  214. if (_failedAttemptsCount > 0) {
  215. _failedAttemptsCount--;
  216. [self incrementFailedAttemptsLabel];
  217. }
  218. }
  219. // workaround for issue on iPad, first responder gets lost after rotation to landscape while in background
  220. - (void)viewDidLayoutSubviews {
  221. [[_textFields objectAtIndex:_currentPanel] becomeFirstResponder];
  222. }
  223. - (void)viewWillDisappear:(BOOL)animated {
  224. [super viewWillDisappear:animated];
  225. _shouldReleaseFirstResponser = YES;
  226. [_enterPasscodeTextField resignFirstResponder];
  227. [_setPasscodeTextField resignFirstResponder];
  228. [_confirmPasscodeTextField resignFirstResponder];
  229. }
  230. #pragma mark -
  231. #pragma mark Private methods
  232. - (void)cancelButtonPressed:(id)sender
  233. {
  234. [self dismissViewControllerAnimated:YES completion:nil];
  235. }
  236. - (void)incrementFailedAttemptsLabel
  237. {
  238. AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
  239. _enterPasscodeTextField.text = @"";
  240. for (int i = 0; i < kPasscodeBoxesCount; i++) {
  241. [[[_boxes objectAtIndex:_currentPanel] objectAtIndex:i] setImage:[self emptyBoxImage]];
  242. }
  243. _failedAttemptsCount += 1;
  244. if (_failedAttemptsCount == 1) {
  245. _failedAttemptsLabel.text = KKPasscodeLockLocalizedString(@"1 Failed Passcode Attempt", @"");
  246. } else {
  247. _failedAttemptsLabel.text = [NSString stringWithFormat:KKPasscodeLockLocalizedString(@"%i Failed Passcode Attempts", @""), _failedAttemptsCount];
  248. }
  249. #pragma clang diagnostic push
  250. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  251. CGSize size = [_failedAttemptsLabel.text sizeWithFont:[UIFont boldSystemFontOfSize:14.0]];
  252. _failedAttemptsLabel.frame = _failedAttemptsView.frame = CGRectMake((self.view.bounds.size.width - (size.width + 40.0)) / 2, 150, size.width + 40.0, size.height + 10.0);
  253. #pragma clang diagnostic pop
  254. CAGradientLayer *gradient = [CAGradientLayer layer];
  255. gradient.frame = _failedAttemptsView.bounds;
  256. gradient.colors = [NSArray arrayWithObjects:
  257. (id)[[UIColor colorWithRed:0.7 green:0.05 blue:0.05 alpha:1.0] CGColor],
  258. (id)[[UIColor colorWithRed:0.8 green:0.2 blue:0.2 alpha:1.0] CGColor], nil];
  259. [_failedAttemptsView.layer insertSublayer:gradient atIndex:0];
  260. _failedAttemptsView.layer.masksToBounds = YES;
  261. _failedAttemptsLabel.hidden = NO;
  262. _failedAttemptsView.hidden = NO;
  263. if (_failedAttemptsCount >= [[KKPasscodeLock sharedLock] attemptsAllowed]) {
  264. if (_eraseData) {
  265. if ([_delegate respondsToSelector:@selector(shouldEraseApplicationData:)]) {
  266. [_delegate shouldEraseApplicationData:self];
  267. }
  268. } else {
  269. if ([_delegate respondsToSelector:@selector(didPasscodeEnteredIncorrectly:)]) {
  270. [_delegate didPasscodeEnteredIncorrectly:self];
  271. }
  272. /* show "erase data" button to give user a chance to reset app if he forgets the passphrase */
  273. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:KKPasscodeLockLocalizedString(@"Erase Data", @"") style:UIBarButtonItemStylePlain target:self action:@selector(eraseDataButtonPressed)];
  274. }
  275. } else {
  276. [[AppGroup userDefaults] setInteger:_failedAttemptsCount forKey:@"FailedCodeAttempts"];
  277. [[AppGroup userDefaults] synchronize];
  278. }
  279. }
  280. - (void)eraseDataButtonPressed {
  281. NSString *title = KKPasscodeLockLocalizedString(@"Erase all data and reset passcode", @"");
  282. NSString *cancel = NSLocalizedString(@"cancel", nil);
  283. NSString *erase = KKPasscodeLockLocalizedString(@"Erase Data", @"");
  284. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
  285. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancel style:UIAlertActionStyleCancel handler:nil];
  286. UIAlertAction *eraseAction = [UIAlertAction actionWithTitle:erase style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
  287. [self eraseData];
  288. }];
  289. [alertController addAction:cancelAction];
  290. [alertController addAction:eraseAction];
  291. [self presentViewController:alertController animated:YES completion:nil];
  292. }
  293. - (void)eraseData {
  294. if ([_delegate respondsToSelector:@selector(shouldEraseApplicationData:)]) {
  295. [_delegate shouldEraseApplicationData:self];
  296. }
  297. }
  298. - (void)moveToNextTableView
  299. {
  300. _currentPanel += 1;
  301. UITableView *oldTableView = [_tableViews objectAtIndex:_currentPanel - 1];
  302. UITableView *newTableView = [_tableViews objectAtIndex:_currentPanel];
  303. newTableView.frame = CGRectMake(oldTableView.frame.origin.x + self.view.bounds.size.width,
  304. oldTableView.frame.origin.y,
  305. oldTableView.frame.size.width,
  306. oldTableView.frame.size.height);
  307. for (int i = 0; i < kPasscodeBoxesCount; i++) {
  308. [[[_boxes objectAtIndex:_currentPanel] objectAtIndex:i] setImage:[self emptyBoxImage]];
  309. }
  310. [UIView beginAnimations:@"" context:nil];
  311. [UIView setAnimationDuration:0.25];
  312. oldTableView.frame = CGRectMake(oldTableView.frame.origin.x - self.view.bounds.size.width, oldTableView.frame.origin.y, oldTableView.frame.size.width, oldTableView.frame.size.height);
  313. newTableView.frame = self.view.frame;
  314. [UIView commitAnimations];
  315. _shouldReleaseFirstResponser = YES;
  316. [[_textFields objectAtIndex:_currentPanel - 1] resignFirstResponder];
  317. _shouldReleaseFirstResponser = NO;
  318. [[_textFields objectAtIndex:_currentPanel] becomeFirstResponder];
  319. }
  320. - (void)moveToPreviousTableView
  321. {
  322. _currentPanel -= 1;
  323. UITableView *oldTableView = [_tableViews objectAtIndex:_currentPanel + 1];
  324. UITableView *newTableView = [_tableViews objectAtIndex:_currentPanel];
  325. newTableView.frame = CGRectMake(oldTableView.frame.origin.x - self.view.bounds.size.width, oldTableView.frame.origin.y, oldTableView.frame.size.width, oldTableView.frame.size.height);
  326. for (int i = 0; i < kPasscodeBoxesCount; i++) {
  327. [[[_boxes objectAtIndex:_currentPanel] objectAtIndex:i] setImage:[self emptyBoxImage]];
  328. }
  329. [UIView beginAnimations:@"" context:nil];
  330. [UIView setAnimationDuration:0.25];
  331. oldTableView.frame = CGRectMake(oldTableView.frame.origin.x + self.view.bounds.size.width, oldTableView.frame.origin.y, oldTableView.frame.size.width, oldTableView.frame.size.height);
  332. newTableView.frame = self.view.frame;
  333. [UIView commitAnimations];
  334. _shouldReleaseFirstResponser = YES;
  335. [[_textFields objectAtIndex:_currentPanel + 1] resignFirstResponder];
  336. _shouldReleaseFirstResponser = NO;
  337. [[_textFields objectAtIndex:_currentPanel] becomeFirstResponder];
  338. }
  339. - (void)nextDigitPressed
  340. {
  341. UITextField* textField = [_textFields objectAtIndex:_currentPanel];
  342. if (![textField.text isEqualToString:@""]) {
  343. if (_mode == KKPasscodeModeSet) {
  344. if ([textField isEqual:_setPasscodeTextField]) {
  345. [self moveToNextTableView];
  346. } else if ([textField isEqual:_confirmPasscodeTextField]) {
  347. if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
  348. _confirmPasscodeTextField.text = @"";
  349. _setPasscodeTextField.text = @"";
  350. _passcodeConfirmationWarningLabel.text = KKPasscodeLockLocalizedString(@"Passcodes did not match. Try again.", @"");
  351. _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 132.0, self.view.bounds.size.width, 60.0);
  352. [self moveToPreviousTableView];
  353. } else {
  354. if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
  355. [KKKeychain setString:@"YES" forKey:@"passcode_on"];
  356. }
  357. if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
  358. [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
  359. }
  360. [self dismissViewControllerAnimated:YES completion:nil];
  361. }
  362. }
  363. } else if (_mode == KKPasscodeModeChange) {
  364. NSString* passcode = [KKKeychain getStringForKey:@"passcode"];
  365. if ([textField isEqual:_enterPasscodeTextField]) {
  366. if ([passcode isEqualToString:_enterPasscodeTextField.text]) {
  367. [self moveToNextTableView];
  368. [[AppGroup userDefaults] setInteger:0 forKey:@"FailedCodeAttempts"];
  369. [[AppGroup userDefaults] synchronize];
  370. } else {
  371. [self incrementFailedAttemptsLabel];
  372. }
  373. } else if ([textField isEqual:_setPasscodeTextField]) {
  374. if ([passcode isEqualToString:_setPasscodeTextField.text]) {
  375. _setPasscodeTextField.text = @"";
  376. _passcodeConfirmationWarningLabel.text = KKPasscodeLockLocalizedString(@"Enter a different passcode. You cannot re-use the same passcode.", @"");
  377. _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 132.0, self.view.bounds.size.width, 60.0);
  378. } else {
  379. _passcodeConfirmationWarningLabel.text = @"";
  380. _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 146.0, self.view.bounds.size.width, 30.0);
  381. [self moveToNextTableView];
  382. }
  383. } else if ([textField isEqual:_confirmPasscodeTextField]) {
  384. if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
  385. _confirmPasscodeTextField.text = @"";
  386. _setPasscodeTextField.text = @"";
  387. _passcodeConfirmationWarningLabel.text = KKPasscodeLockLocalizedString(@"Passcodes did not match. Try again.", "");
  388. _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 132.0, self.view.bounds.size.width, 60.0);
  389. [self moveToPreviousTableView];
  390. } else {
  391. if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
  392. [KKKeychain setString:@"YES" forKey:@"passcode_on"];
  393. }
  394. if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
  395. [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
  396. }
  397. [self dismissViewControllerAnimated:YES completion:nil];
  398. }
  399. }
  400. }
  401. }
  402. }
  403. - (void)validatePasscode:(UITextField*)textField
  404. {
  405. if (_mode == KKPasscodeModeDisabled) {
  406. NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
  407. if ([_enterPasscodeTextField.text isEqualToString:passcode]) {
  408. if ([KKKeychain setString:@"NO" forKey:@"passcode_on"]) {
  409. [KKKeychain setString:@"" forKey:@"passcode"];
  410. }
  411. if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
  412. [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
  413. }
  414. [[AppGroup userDefaults] setInteger:0 forKey:@"FailedCodeAttempts"];
  415. [[AppGroup userDefaults] synchronize];
  416. [self dismissViewControllerAnimated:YES completion:nil];
  417. } else {
  418. [self incrementFailedAttemptsLabel];
  419. }
  420. } else if (_mode == KKPasscodeModeEnter) {
  421. NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
  422. if ([_enterPasscodeTextField.text isEqualToString:passcode]) {
  423. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  424. [UIView beginAnimations:@"fadeIn" context:nil];
  425. [UIView setAnimationDelay:0.25];
  426. [UIView setAnimationDuration:0.5];
  427. [UIView commitAnimations];
  428. }
  429. [[KKPasscodeLock sharedLock] updateLastUnlockTime];
  430. if ([_delegate respondsToSelector:@selector(didPasscodeEnteredCorrectly:)]) {
  431. [_delegate performSelector:@selector(didPasscodeEnteredCorrectly:) withObject:self];
  432. }
  433. [[AppGroup userDefaults] setInteger:0 forKey:@"FailedCodeAttempts"];
  434. [[AppGroup userDefaults] synchronize];
  435. [self dismissViewControllerAnimated:YES completion:^{
  436. if ([_delegate respondsToSelector:@selector(didPasscodeViewDismiss:)]) {
  437. [_delegate performSelector:@selector(didPasscodeViewDismiss:) withObject:self];
  438. }
  439. }];
  440. } else {
  441. [self performSelector:@selector(incrementFailedAttemptsLabel) withObject:nil afterDelay:0.5];
  442. }
  443. } else if (_mode == KKPasscodeModeChange) {
  444. NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
  445. if ([textField isEqual:_enterPasscodeTextField]) {
  446. if ([passcode isEqualToString:_enterPasscodeTextField.text]) {
  447. [[AppGroup userDefaults] setInteger:0 forKey:@"FailedCodeAttempts"];
  448. [[AppGroup userDefaults] synchronize];
  449. [self moveToNextTableView];
  450. } else {
  451. [self incrementFailedAttemptsLabel];
  452. }
  453. } else if ([textField isEqual:_setPasscodeTextField]) {
  454. if ([passcode isEqualToString:_setPasscodeTextField.text]) {
  455. _setPasscodeTextField.text = @"";
  456. for (int i = 0; i < kPasscodeBoxesCount; i++) {
  457. [[[_boxes objectAtIndex:_currentPanel] objectAtIndex:i] setImage:[self emptyBoxImage]];
  458. }
  459. _passcodeConfirmationWarningLabel.text = KKPasscodeLockLocalizedString(@"Enter a different passcode. You cannot re-use the same passcode.", @"");
  460. _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 132.0, self.view.bounds.size.width, 60.0);
  461. } else {
  462. _passcodeConfirmationWarningLabel.text = @"";
  463. _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 146.0, self.view.bounds.size.width, 30.0);
  464. [self moveToNextTableView];
  465. }
  466. } else if ([textField isEqual:_confirmPasscodeTextField]) {
  467. if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
  468. _confirmPasscodeTextField.text = @"";
  469. _setPasscodeTextField.text = @"";
  470. _passcodeConfirmationWarningLabel.text = KKPasscodeLockLocalizedString(@"Passcodes did not match. Try again.", @"");
  471. _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 132.0, self.view.bounds.size.width, 60.0);
  472. [self moveToPreviousTableView];
  473. } else {
  474. if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
  475. [KKKeychain setString:@"YES" forKey:@"passcode_on"];
  476. }
  477. if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
  478. [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
  479. }
  480. [self dismissViewControllerAnimated:YES completion:nil];
  481. }
  482. }
  483. } else if ([textField isEqual:_setPasscodeTextField]) {
  484. [self moveToNextTableView];
  485. } else if ([textField isEqual:_confirmPasscodeTextField]) {
  486. if (![_confirmPasscodeTextField.text isEqualToString:_setPasscodeTextField.text]) {
  487. _confirmPasscodeTextField.text = @"";
  488. _setPasscodeTextField.text = @"";
  489. _passcodeConfirmationWarningLabel.text = KKPasscodeLockLocalizedString(@"Passcodes did not match. Try again.", @"");
  490. _passcodeConfirmationWarningLabel.frame = CGRectMake(0.0, 132.0, self.view.bounds.size.width, 60.0);
  491. [self moveToPreviousTableView];
  492. } else {
  493. if ([KKKeychain setString:_setPasscodeTextField.text forKey:@"passcode"]) {
  494. [KKKeychain setString:@"YES" forKey:@"passcode_on"];
  495. }
  496. if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
  497. [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
  498. }
  499. [self dismissViewControllerAnimated:YES completion:nil];
  500. }
  501. }
  502. }
  503. - (void)doneButtonPressed
  504. {
  505. UITextField *textField = [_textFields objectAtIndex:_currentPanel];
  506. [self validatePasscode:textField];
  507. }
  508. - (UIView*)headerViewForTextField:(UITextField*)textField
  509. {
  510. [self.view addSubview:textField];
  511. UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, 70.0)];
  512. UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 28.0, self.view.bounds.size.width, 30.0)];
  513. headerLabel.backgroundColor = [UIColor clearColor];
  514. #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
  515. headerLabel.textAlignment = UITextAlignmentCenter;
  516. #else
  517. headerLabel.textAlignment = NSTextAlignmentCenter;
  518. #endif
  519. if ([Colors areCustomized]) {
  520. headerLabel.textColor = [Colors fontNormal];
  521. headerLabel.shadowColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.4 alpha:1.0];
  522. [Colors updateKeyboardAppearanceFor:textField];
  523. } else {
  524. headerLabel.textColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.4 alpha:1.0];
  525. headerLabel.shadowColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
  526. }
  527. headerLabel.font = [UIFont boldSystemFontOfSize:17.0];
  528. headerLabel.shadowOffset = CGSizeMake(0, 1.0);
  529. if ([textField isEqual:_setPasscodeTextField]) {
  530. _passcodeConfirmationWarningLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 146.0, self.view.bounds.size.width, 30.0)];
  531. _passcodeConfirmationWarningLabel.textColor = headerLabel.textColor;
  532. _passcodeConfirmationWarningLabel.backgroundColor = [UIColor clearColor];
  533. #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
  534. _passcodeConfirmationWarningLabel.textAlignment = UITextAlignmentCenter;
  535. #else
  536. _passcodeConfirmationWarningLabel.textAlignment = NSTextAlignmentCenter;
  537. #endif
  538. _passcodeConfirmationWarningLabel.font = [UIFont systemFontOfSize:14.0];
  539. _passcodeConfirmationWarningLabel.shadowOffset = CGSizeMake(0, 1.0);
  540. _passcodeConfirmationWarningLabel.shadowColor = headerLabel.shadowColor;
  541. _passcodeConfirmationWarningLabel.text = @"";
  542. _passcodeConfirmationWarningLabel.numberOfLines = 0;
  543. #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
  544. _passcodeConfirmationWarningLabel.lineBreakMode = UILineBreakModeWordWrap;
  545. #else
  546. _passcodeConfirmationWarningLabel.lineBreakMode = NSLineBreakByWordWrapping;
  547. #endif
  548. [headerView addSubview:_passcodeConfirmationWarningLabel];
  549. }
  550. if ([textField isEqual:_enterPasscodeTextField]) {
  551. _failedAttemptsView = [[UIView alloc] init];
  552. _failedAttemptsLabel = [[UILabel alloc] init];
  553. _failedAttemptsLabel.backgroundColor = [UIColor clearColor];
  554. _failedAttemptsLabel.textColor = [UIColor whiteColor];
  555. _failedAttemptsLabel.text = @"";
  556. _failedAttemptsLabel.font = [UIFont boldSystemFontOfSize:14.0];
  557. #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
  558. _failedAttemptsLabel.textAlignment = UITextAlignmentCenter;
  559. #else
  560. _failedAttemptsLabel.textAlignment = NSTextAlignmentCenter;
  561. #endif
  562. _failedAttemptsLabel.shadowOffset = CGSizeMake(0, -1.0);
  563. _failedAttemptsLabel.shadowColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0];
  564. _failedAttemptsView.layer.cornerRadius = 14;
  565. _failedAttemptsView.layer.borderWidth = 1.0;
  566. _failedAttemptsView.layer.borderColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.25] CGColor];
  567. _failedAttemptsLabel.hidden = YES;
  568. _failedAttemptsView.hidden = YES;
  569. _failedAttemptsView.layer.masksToBounds = YES;
  570. _failedAttemptsLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  571. _failedAttemptsView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
  572. [headerView addSubview:_failedAttemptsView];
  573. [headerView addSubview:_failedAttemptsLabel];
  574. }
  575. if (_mode == KKPasscodeModeSet) {
  576. if ([textField isEqual:_enterPasscodeTextField]) {
  577. headerLabel.text = KKPasscodeLockLocalizedString(@"Enter your passcode", @"");
  578. } else if ([textField isEqual:_setPasscodeTextField]) {
  579. headerLabel.text = KKPasscodeLockLocalizedString(@"Enter a passcode", @"");
  580. } else if ([textField isEqual:_confirmPasscodeTextField]) {
  581. headerLabel.text = KKPasscodeLockLocalizedString(@"Re-enter your passcode", @"");
  582. }
  583. } else if (_mode == KKPasscodeModeDisabled) {
  584. headerLabel.text = KKPasscodeLockLocalizedString(@"Enter your passcode", @"");
  585. } else if (_mode == KKPasscodeModeChange) {
  586. if ([textField isEqual:_enterPasscodeTextField]) {
  587. headerLabel.text = KKPasscodeLockLocalizedString(@"Enter your old passcode", @"");
  588. } else if ([textField isEqual:_setPasscodeTextField]) {
  589. headerLabel.text = KKPasscodeLockLocalizedString(@"Enter your new passcode", @"");
  590. } else {
  591. headerLabel.text = KKPasscodeLockLocalizedString(@"Re-enter your new passcode", @"");
  592. }
  593. } else {
  594. headerLabel.text = KKPasscodeLockLocalizedString(@"Enter your passcode", @"");
  595. }
  596. headerLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
  597. [headerView addSubview:headerLabel];
  598. return headerView;
  599. }
  600. - (NSArray*)boxes
  601. {
  602. NSMutableArray* squareViews = [NSMutableArray array];
  603. CGFloat squareX = 5.0;
  604. for (int i = 0; i < kPasscodeBoxesCount; i++) {
  605. UIImageView *square = [[UIImageView alloc] initWithImage:[self emptyBoxImage]];
  606. square.frame = CGRectMake(squareX, 0, kPasscodeBoxWidth, kPasscodeBoxHeight);
  607. [squareViews addObject:square];
  608. squareX += 71.0;
  609. }
  610. return [NSArray arrayWithArray:squareViews];
  611. }
  612. - (UIImage*)emptyBoxImage {
  613. static UIImage* emptyBoxImage;
  614. if (emptyBoxImage == nil) {
  615. emptyBoxImage =[BundleUtil imageNamed:@"KKPasscodeLock.bundle/box7_empty.png"];
  616. }
  617. return emptyBoxImage;
  618. }
  619. - (UIImage*)filledBoxImage {
  620. static UIImage* filledBoxImage;
  621. if (filledBoxImage == nil) {
  622. filledBoxImage =[BundleUtil imageNamed:@"KKPasscodeLock.bundle/box7_filled.png"];
  623. }
  624. return filledBoxImage;
  625. }
  626. - (CGFloat)iOS7TableTopInset {
  627. if ([UIScreen mainScreen].bounds.size.height > 667)
  628. return self.navigationController.navigationBar.frame.size.height+150; /* iPhone 6 Plus */
  629. else if ([UIScreen mainScreen].bounds.size.height > 568)
  630. return self.navigationController.navigationBar.frame.size.height+130; /* iPhone 6 */
  631. else if ([UIScreen mainScreen].bounds.size.height > 480)
  632. return self.navigationController.navigationBar.frame.size.height+60; /* iPhone 5 */
  633. else
  634. return self.navigationController.navigationBar.frame.size.height+30;
  635. }
  636. #pragma mark -
  637. #pragma mark UITableViewDataSource methods
  638. - (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
  639. {
  640. return 0;
  641. }
  642. - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
  643. {
  644. return 1;
  645. }
  646. - (UITableViewCell*)tableView:(UITableView*)aTableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
  647. {
  648. static NSString* CellIdentifier = @"KKPasscodeViewControllerCell";
  649. UITableViewCell* cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
  650. if (cell == nil) {
  651. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
  652. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  653. }
  654. if ([aTableView isEqual:_enterPasscodeTableView]) {
  655. cell.accessoryView = _enterPasscodeTextField;
  656. } else if ([aTableView isEqual:_setPasscodeTableView]) {
  657. cell.accessoryView = _setPasscodeTextField;
  658. } else if ([aTableView isEqual:_confirmPasscodeTableView]) {
  659. cell.accessoryView = _confirmPasscodeTextField;
  660. }
  661. return cell;
  662. }
  663. #pragma mark -
  664. #pragma mark UITextFieldDelegate methods
  665. - (BOOL)textFieldShouldReturn:(UITextField*)textField
  666. {
  667. if ([textField isEqual:[_textFields lastObject]]) {
  668. [self doneButtonPressed];
  669. } else {
  670. [self nextDigitPressed];
  671. }
  672. return NO;
  673. }
  674. - (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string
  675. {
  676. NSString *result = [textField.text stringByReplacingCharactersInRange:range withString:string];
  677. textField.text = result;
  678. for (int i = 0; i < kPasscodeBoxesCount; i++) {
  679. UIImageView *square = [[_boxes objectAtIndex:_currentPanel] objectAtIndex:i];
  680. if (i < [result length]) {
  681. square.image = [self filledBoxImage];
  682. } else {
  683. square.image = [self emptyBoxImage];
  684. }
  685. }
  686. if ([result length] == kPasscodeBoxesCount) {
  687. [self performSelector:@selector(validatePasscode:) withObject:textField afterDelay:0.1];
  688. }
  689. return NO;
  690. }
  691. - (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
  692. return _shouldReleaseFirstResponser;
  693. }
  694. #pragma mark -
  695. #pragma mark Memory management
  696. @end