JKLLockScreenViewController.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. // This file is based on third party code, see below for the original author
  2. // and original license.
  3. // Modifications are (c) by Threema GmbH and licensed under the AGPLv3.
  4. // See Resources/License.html for original license
  5. #import "JKLLockScreenViewController.h"
  6. #import "JKLLockScreenPincodeView.h"
  7. #import "JKLLockScreenNumber.h"
  8. #import <AudioToolbox/AudioToolbox.h>
  9. #import <LocalAuthentication/LocalAuthentication.h>
  10. //------------------ Threema edit begin ---------------------------
  11. #import "BundleUtil.h"
  12. #import "LicenseStore.h"
  13. #import "AppGroup.h"
  14. #import "KKPasscodeLock.h"
  15. #import "KKKeychain.h"
  16. #import "UIImage+ColoredImage.h"
  17. #import "ThreemaFramework/ThreemaFramework-swift.h"
  18. //------------------ Threema edit end ---------------------------
  19. static const NSTimeInterval LSVSwipeAnimationDuration = 0.3f;
  20. static const NSTimeInterval LSVDismissWaitingDuration = 0.4f;
  21. static const NSTimeInterval LSVShakeAnimationDuration = 0.5f;
  22. static const NSUInteger oldMaxLength = 4;
  23. static const NSUInteger newMaxLength = 6;
  24. //------------------ Threema edit begin ---------------------------
  25. @interface JKLLockScreenViewController()<JKLLockScreenPincodeViewDelegate, UITextFieldDelegate> {
  26. //------------------ Threema edit end ---------------------------
  27. NSString * _confirmPincode;
  28. LockScreenMode _prevLockScreenMode;
  29. //------------------ Threema edit begin ---------------------------
  30. NSInteger _failedAttemptsCount;
  31. BOOL _eraseData;
  32. BOOL _passcodeLockOn;
  33. BOOL _sameAsOldPassword;
  34. //------------------ Threema edit end ---------------------------
  35. }
  36. @property (nonatomic, weak) IBOutlet UILabel * titleLabel;
  37. @property (nonatomic, weak) IBOutlet UILabel * subtitleLabel;
  38. @property (nonatomic, weak) IBOutlet UIButton * cancelButton;
  39. @property (nonatomic, weak) IBOutlet UIButton * deleteButton;
  40. //------------------ Threema edit begin ---------------------------
  41. @property (nonatomic, weak) IBOutlet UITextField *textField;
  42. @property (nonatomic, weak) IBOutlet UIImageView *threemaLogo;
  43. @property (nonatomic, weak) IBOutlet UIButton *eraseDataButton;
  44. //------------------ Threema edit end ---------------------------
  45. @property (strong, nonatomic) IBOutletCollection(JKLLockScreenNumber) NSArray *numberButtons;
  46. @property (nonatomic, weak) IBOutlet JKLLockScreenPincodeView * pincodeView;
  47. @end
  48. @implementation JKLLockScreenViewController
  49. //------------------ Threema edit begin ---------------------------
  50. - (void)loadView {
  51. [super loadView];
  52. _failedAttemptsCount = [[AppGroup userDefaults] integerForKey:@"FailedCodeAttempts"];
  53. }
  54. - (void)viewWillAppear:(BOOL)animated {
  55. [super viewWillAppear:animated];
  56. //------------------ Threema edit begin ---------------------------
  57. [_cancelButton setTitle:[BundleUtil localizedStringForKey:@"cancel"] forState:UIControlStateNormal];
  58. [_deleteButton setTitle:[BundleUtil localizedStringForKey:@"delete"] forState:UIControlStateNormal];
  59. [_eraseDataButton setTitle:KKPasscodeLockLocalizedString(@"Erase Data", @"") forState:UIControlStateNormal];
  60. //------------------ Threema edit end ---------------------------
  61. _passcodeLockOn = [[KKKeychain getStringForKey:@"passcode_on"] isEqualToString:@"YES"];
  62. _eraseData = [[KKPasscodeLock sharedLock] eraseOption] && [[KKKeychain getStringForKey:@"erase_data_on"] isEqualToString:@"YES"];
  63. _titleLabel.textColor = [Colors white];
  64. _titleLabel.shadowColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.4 alpha:0.6];
  65. _subtitleLabel.textColor = [Colors white];
  66. _subtitleLabel.shadowColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.4 alpha:0.6];
  67. [_eraseDataButton setTitleColor:[Colors red] forState:UIControlStateNormal];
  68. [_eraseDataButton setTitleShadowColor:[UIColor colorWithRed:0.3 green:0.3 blue:0.4 alpha:0.6] forState:UIControlStateNormal];
  69. [self.view setBackgroundColor:[Colors backgroundDark]];
  70. //------------------ Threema edit begin ---------------------------
  71. if ([LicenseStore requiresLicenseKey] == NO) {
  72. _threemaLogo.image = [BundleUtil imageNamed:@"PasscodeLogo"];
  73. } else {
  74. _threemaLogo.image = [BundleUtil imageNamed:@"PasscodeLogoWork"];
  75. }
  76. //------------------ Threema edit end ---------------------------
  77. [Colors updateNavigationBar:self.navigationController.navigationBar];
  78. _pincodeView.pincodeColor = [Colors main];
  79. _tintColor = [Colors main];
  80. [self tintSubviewsWithColor:_tintColor];
  81. }
  82. //------------------ Threema edit end ---------------------------
  83. - (void)viewDidLoad {
  84. [super viewDidLoad];
  85. //------------------ Threema edit begin ---------------------------
  86. _cancelButton.titleLabel.adjustsFontSizeToFitWidth = YES;
  87. _deleteButton.titleLabel.adjustsFontSizeToFitWidth = YES;
  88. UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
  89. [_threemaLogo addGestureRecognizer:lpgr];
  90. switch (_lockScreenMode) {
  91. case LockScreenModeVerification:
  92. case LockScreenModeNormal: {
  93. // [일반 모드] Cancel 버튼 감춤
  94. [_cancelButton setHidden:YES];
  95. [_eraseDataButton setHidden:YES];
  96. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter your passcode", @"")];
  97. [self lsv_updateSubtitle:nil];
  98. _threemaLogo.userInteractionEnabled = YES;
  99. _pincodeView.maxPincodeLength = [self is4DigitCodeSet] ? oldMaxLength : newMaxLength;
  100. break;
  101. }
  102. case LockScreenModeExtension: {
  103. // [일반 모드] Cancel 버튼 감춤
  104. [_cancelButton setHidden:NO];
  105. [_eraseDataButton setHidden:YES];
  106. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter your passcode", @"")];
  107. [self lsv_updateSubtitle:nil];
  108. _threemaLogo.userInteractionEnabled = YES;
  109. _pincodeView.maxPincodeLength = [self is4DigitCodeSet] ? oldMaxLength : newMaxLength;
  110. break;
  111. }
  112. case LockScreenModeNew: {
  113. // [신규 모드]
  114. [_eraseDataButton setHidden:YES];
  115. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter a passcode", @"")];
  116. [self lsv_updateSubtitle:nil];
  117. _threemaLogo.userInteractionEnabled = NO;
  118. [_textField resignFirstResponder];
  119. _pincodeView.maxPincodeLength = newMaxLength;
  120. break;
  121. }
  122. case LockScreenModeChange:
  123. [_eraseDataButton setHidden:YES];
  124. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter your new passcode", @"")];
  125. [self lsv_updateSubtitle:nil];
  126. _threemaLogo.userInteractionEnabled = YES;
  127. _pincodeView.maxPincodeLength = newMaxLength;
  128. break;
  129. case LockScreenModeChangeCheck:
  130. [_eraseDataButton setHidden:YES];
  131. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter your old passcode", @"")];
  132. [self lsv_updateSubtitle:nil];
  133. _threemaLogo.userInteractionEnabled = YES;
  134. _pincodeView.maxPincodeLength = [self is4DigitCodeSet] ? oldMaxLength : newMaxLength;
  135. break;
  136. case LockScreenModeDisable:
  137. [_eraseDataButton setHidden:YES];
  138. [_cancelButton setHidden:NO];
  139. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter your passcode", @"")];
  140. [self lsv_updateSubtitle:nil];
  141. _threemaLogo.userInteractionEnabled = YES;
  142. _pincodeView.maxPincodeLength = [self is4DigitCodeSet] ? oldMaxLength : newMaxLength;
  143. break;
  144. }
  145. [_pincodeView setNeedsDisplay];
  146. //------------------ Threema edit end ---------------------------
  147. }
  148. - (void)viewDidAppear:(BOOL)animated {
  149. [super viewDidAppear:animated];
  150. // [일반모드] 였을 경우
  151. BOOL isModeNormal = (_lockScreenMode == LockScreenModeNormal || _lockScreenMode == LockScreenModeExtension);
  152. if (isModeNormal && [_delegate respondsToSelector:@selector(allowTouchIDLockScreenViewController:)]) {
  153. if ([_dataSource allowTouchIDLockScreenViewController:self]) {
  154. // Touch ID 암호 입력창 호출
  155. [self lsv_policyDeviceOwnerAuthentication];
  156. }
  157. }
  158. }
  159. /**
  160. * Changes buttons tint color
  161. *
  162. * @param color tint color for buttons
  163. */
  164. - (void)tintSubviewsWithColor: (UIColor *) color{
  165. [_cancelButton setTitleColor:color forState:UIControlStateNormal];
  166. [_deleteButton setTitleColor:color forState:UIControlStateNormal];
  167. [_pincodeView setPincodeColor:color];
  168. for (JKLLockScreenNumber * number in _numberButtons)
  169. {
  170. [number setTintColor:color];
  171. [number setTitleColor:[Colors white] forState:UIControlStateNormal];
  172. }
  173. }
  174. /**
  175. Touch ID 창을 호출하는 메소드
  176. */
  177. - (void)lsv_policyDeviceOwnerAuthentication {
  178. NSError * error = nil;
  179. LAContext * context = [[LAContext alloc] init];
  180. // check if the policy can be evaluated
  181. if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
  182. // evaluate
  183. [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
  184. localizedReason:NSLocalizedStringFromTable(@"Pincode TouchID", @"JKLockScreen", nil)
  185. reply:^(BOOL success, NSError * authenticationError) {
  186. if (success) {
  187. [self lsv_unlockDelayDismissViewController:LSVDismissWaitingDuration];
  188. }
  189. else {
  190. NSLog(@"LAContext::Authentication Error : %@", authenticationError);
  191. }
  192. }];
  193. }
  194. else {
  195. NSLog(@"LAContext::Policy Error : %@", [error localizedDescription]);
  196. }
  197. }
  198. /**
  199. 일정 시간 딜레이 후 창을 dismiss 하는 메소드
  200. @param NSTimeInterval 딜레이 시간
  201. */
  202. - (void)lsv_unlockDelayDismissViewController:(NSTimeInterval)delay {
  203. __weak id weakSelf = self;
  204. [_pincodeView wasCompleted];
  205. // 인증이 완료된 후 창이 dismiss될 때
  206. // 너무 빨리 dimiss되면 잔상처럼 남으므로 일정시간 딜레이 걸어서 dismiss 함
  207. dispatch_time_t delayInSeconds = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
  208. dispatch_after(delayInSeconds, dispatch_get_main_queue(), ^(void){
  209. [self dismissViewControllerAnimated:NO completion:^{
  210. if ([_delegate respondsToSelector:@selector(unlockWasSuccessfulLockScreenViewController:)]) {
  211. [_delegate unlockWasSuccessfulLockScreenViewController:weakSelf];
  212. }
  213. if ([_delegate respondsToSelector:@selector(didPasscodeEnteredCorrectly:)]) {
  214. [_delegate didPasscodeEnteredCorrectly:weakSelf];
  215. }
  216. }];
  217. });
  218. }
  219. /**
  220. 핀코드가 일치하는지 반판하는 메소드: [확인모드]와 [일반모드]가 다르다
  221. @param NSString PIN code
  222. @return BOOL 암호 유효성
  223. */
  224. - (BOOL)lsv_isPincodeValid:(NSString *)pincode {
  225. //------------------ Threema edit begin ---------------------------
  226. // // [확인모드]일 경우, Confirm Pincode와 비교
  227. // if (_lockScreenMode == LockScreenModeVerification) {
  228. // return [_confirmPincode isEqualToString:pincode];
  229. // }
  230. //
  231. // // [신규모드], [변경모드]일 경우 기존 Pincode와 비교
  232. // return [_dataSource lockScreenViewController:self pincode:pincode];
  233. //------------------ Threema edit end ---------------------------
  234. //------------------ Threema edit begin ---------------------------
  235. if (_lockScreenMode == LockScreenModeNormal || _lockScreenMode == LockScreenModeExtension || _lockScreenMode == LockScreenModeDisable || _lockScreenMode == LockScreenModeChangeCheck) {
  236. NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
  237. return [pincode isEqualToString:passcode];
  238. }
  239. else if (_lockScreenMode == LockScreenModeChange) {
  240. NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
  241. return ![passcode isEqualToString:pincode];
  242. }
  243. else if (_lockScreenMode == LockScreenModeVerification) {
  244. return [pincode isEqualToString:_confirmPincode];
  245. }
  246. return NO;
  247. //------------------ Threema edit end ---------------------------
  248. }
  249. /**
  250. 타이틀과 서브타이틀을 변경하는 메소드
  251. @param NSString 주 제목
  252. @param NSString 서브 제목
  253. */
  254. - (void)lsv_updateTitle:(NSString *)title {
  255. [_titleLabel setText:title];
  256. }
  257. - (void)lsv_updateSubtitle:(NSString *)subtitle {
  258. [_subtitleLabel setText:subtitle];
  259. }
  260. /**
  261. 잠금 해제에 성공했을 경우 발생하는 메소드
  262. @param NSString PIN code
  263. */
  264. - (void)lsv_unlockScreenSuccessful:(NSString *)pincode {
  265. [self dismissViewControllerAnimated:YES completion:^{
  266. if ([_delegate respondsToSelector:@selector(unlockWasSuccessfulLockScreenViewController:pincode:)]) {
  267. [_delegate unlockWasSuccessfulLockScreenViewController:self pincode:pincode];
  268. }
  269. //------------------ Threema edit begin ---------------------------
  270. if ([_delegate respondsToSelector:@selector(didPasscodeViewDismiss:)]) {
  271. [_delegate performSelector:@selector(didPasscodeViewDismiss:) withObject:self];
  272. }
  273. //------------------ Threema edit end ---------------------------
  274. }];
  275. }
  276. /**
  277. 잠금 해제에 실패했을 경우 발생하는 메소드
  278. */
  279. - (void)lsv_unlockScreenFailure {
  280. if (_lockScreenMode != LockScreenModeVerification) {
  281. if ([_delegate respondsToSelector:@selector(unlockWasFailureLockScreenViewController:)]) {
  282. [_delegate unlockWasFailureLockScreenViewController:self];
  283. }
  284. }
  285. // 디바이스 진동
  286. AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
  287. // make shake animation
  288. CAAnimation * shake = [self lsv_makeShakeAnimation];
  289. [_pincodeView.layer addAnimation:shake forKey:@"shake"];
  290. [_pincodeView setEnabled:NO];
  291. //------------------ Threema edit begin ---------------------------
  292. [_subtitleLabel setText:KKPasscodeLockLocalizedString(@"Passcodes did not match. Try again.", @"")];
  293. //------------------ Threema edit end ---------------------------
  294. dispatch_time_t delayInSeconds = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(LSVShakeAnimationDuration * NSEC_PER_SEC));
  295. dispatch_after(delayInSeconds, dispatch_get_main_queue(), ^(void){
  296. [_pincodeView setEnabled:YES];
  297. [_pincodeView initPincode];
  298. switch (_lockScreenMode) {
  299. //------------------ Threema edit begin ---------------------------
  300. case LockScreenModeChangeCheck:
  301. case LockScreenModeExtension:
  302. case LockScreenModeNormal: {
  303. _failedAttemptsCount += 1;
  304. _pincodeView.maxPincodeLength = [self is4DigitCodeSet] ? oldMaxLength : newMaxLength;
  305. if (_failedAttemptsCount == 1) {
  306. [self lsv_updateSubtitle:KKPasscodeLockLocalizedString(@"1 Failed Passcode Attempt", @"")];
  307. } else {
  308. [self lsv_updateSubtitle:[NSString stringWithFormat:KKPasscodeLockLocalizedString(@"%i Failed Passcode Attempts", @""), _failedAttemptsCount]];
  309. }
  310. if (_failedAttemptsCount >= [[KKPasscodeLock sharedLock] attemptsAllowed]) {
  311. if (_eraseData) {
  312. if ([_delegate respondsToSelector:@selector(shouldEraseApplicationData:)]) {
  313. [_delegate shouldEraseApplicationData:self];
  314. }
  315. } else {
  316. if ([_delegate respondsToSelector:@selector(didPasscodeEnteredIncorrectly:)]) {
  317. [_delegate didPasscodeEnteredIncorrectly:self];
  318. }
  319. if (_lockScreenMode == LockScreenModeNormal) {
  320. _eraseDataButton.hidden = NO;
  321. }
  322. /* show "erase data" button to give user a chance to reset app if he forgets the passphrase */
  323. // self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:KKPasscodeLockLocalizedString(@"Erase Data", @"") style:UIBarButtonItemStylePlain target:self action:@selector(eraseDataButtonPressed)];
  324. }
  325. } else {
  326. [[AppGroup userDefaults] setInteger:_failedAttemptsCount forKey:@"FailedCodeAttempts"];
  327. [[AppGroup userDefaults] synchronize];
  328. }
  329. _threemaLogo.userInteractionEnabled = YES;
  330. break;
  331. }
  332. case LockScreenModeNew: {
  333. _pincodeView.maxPincodeLength = newMaxLength;
  334. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter a passcode", @"")];
  335. [self lsv_updateSubtitle:nil];
  336. _threemaLogo.userInteractionEnabled = NO;
  337. [_textField resignFirstResponder];
  338. break;
  339. }
  340. case LockScreenModeChange:
  341. _pincodeView.maxPincodeLength = newMaxLength;
  342. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter your new passcode", @"")];
  343. if (_sameAsOldPassword) {
  344. [self lsv_updateSubtitle:KKPasscodeLockLocalizedString(@"Enter a different passcode. You cannot re-use the same passcode.", @"")];
  345. } else {
  346. [self lsv_updateSubtitle:KKPasscodeLockLocalizedString(@"Passcodes did not match. Try again.", @"")];
  347. }
  348. _threemaLogo.userInteractionEnabled = NO;
  349. [_textField resignFirstResponder];
  350. break;
  351. default:
  352. break;
  353. }
  354. //------------------ Threema edit end ---------------------------
  355. });
  356. }
  357. /**
  358. 쉐이크 에니메이션을 생성하는 메소드
  359. @return CAAnimation
  360. */
  361. - (CAAnimation *)lsv_makeShakeAnimation {
  362. CAKeyframeAnimation * shake = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"];
  363. [shake setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
  364. [shake setDuration:LSVShakeAnimationDuration];
  365. [shake setValues:@[ @(-20), @(20), @(-20), @(20), @(-10), @(10), @(-5), @(5), @(0) ]];
  366. return shake;
  367. }
  368. /**
  369. 서브 타이틀과 PincodeView를 애니메이션 하는 메소드
  370. ! PincodeView는 제약이 서브타이틀과 같이 묶여 있으므로 따로 해주지 않아도 됨
  371. 1차 : 화면 왼쪽 끝으로 이동 with Animation
  372. 2차 : 화면 오른쪽 끝으로 이동 without Animation
  373. 3차 : 화면 가운데로 이동 with Animation
  374. */
  375. - (void)lsv_swipeSubtitleAndPincodeView {
  376. __weak UIView * weakView = self.view;
  377. __weak UIView * weakCode = _pincodeView;
  378. [(id)weakCode setEnabled:NO];
  379. CGFloat width = CGRectGetWidth([self view].bounds);
  380. NSLayoutConstraint * centerX = [self lsv_findLayoutConstraint:weakView childView:_subtitleLabel attribute:NSLayoutAttributeCenterX];
  381. centerX.constant = width;
  382. [UIView animateWithDuration:LSVSwipeAnimationDuration animations:^{
  383. [weakView layoutIfNeeded];
  384. } completion:^(BOOL finished) {
  385. [(id)weakCode initPincode];
  386. centerX.constant = -width;
  387. [weakView layoutIfNeeded];
  388. centerX.constant = 0;
  389. [UIView animateWithDuration:LSVSwipeAnimationDuration animations:^{
  390. [weakView layoutIfNeeded];
  391. } completion:^(BOOL finished) {
  392. [(id)weakCode setEnabled:YES];
  393. }];
  394. }];
  395. }
  396. #pragma mark -
  397. #pragma mark NSLayoutConstraint
  398. - (NSLayoutConstraint *)lsv_findLayoutConstraint:(UIView *)superview childView:(UIView *)childView attribute:(NSLayoutAttribute)attribute {
  399. for (NSLayoutConstraint * constraint in superview.constraints) {
  400. if (constraint.firstItem == superview && constraint.secondItem == childView && constraint.firstAttribute == attribute) {
  401. return constraint;
  402. }
  403. }
  404. return nil;
  405. }
  406. #pragma mark -
  407. #pragma mark IBAction
  408. - (IBAction)onNumberClicked:(id)sender {
  409. NSInteger number = [((UIButton *)sender) tag];
  410. [_pincodeView appendingPincode:[@(number) description]];
  411. }
  412. - (IBAction)onCancelClicked:(id)sender {
  413. if ([_delegate respondsToSelector:@selector(unlockWasCancelledLockScreenViewController:)]) {
  414. [_delegate unlockWasCancelledLockScreenViewController:self];
  415. }
  416. [self dismissViewControllerAnimated:YES completion:NULL];
  417. }
  418. - (IBAction)onDeleteClicked:(id)sender {
  419. [_pincodeView removeLastPincode];
  420. }
  421. //------------------ Threema edit begin ---------------------------
  422. - (void)handleLongPress:(UIGestureRecognizer *)gestureRecognizer {
  423. if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
  424. if (_textField.isFirstResponder) {
  425. [_textField resignFirstResponder];
  426. } else {
  427. [_textField becomeFirstResponder];
  428. }
  429. }
  430. }
  431. - (IBAction)onEraseDataClicked:(id)sender {
  432. [self eraseDataButtonPressed];
  433. }
  434. //------------------ Threema edit end ---------------------------
  435. #pragma mark -
  436. #pragma mark JKLLockScreenPincodeViewDelegate
  437. - (void)lockScreenPincodeView:(JKLLockScreenPincodeView *)lockScreenPincodeView pincode:(NSString *)pincode {
  438. //------------------ Threema edit begin ---------------------------
  439. if (_lockScreenMode == LockScreenModeDisable) {
  440. _pincodeView.maxPincodeLength = [self is4DigitCodeSet] ? oldMaxLength : newMaxLength;
  441. if ([self lsv_isPincodeValid:pincode]) {
  442. if ([KKKeychain setString:@"NO" forKey:@"passcode_on"]) {
  443. [KKKeychain setString:@"" forKey:@"passcode"];
  444. }
  445. if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
  446. [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
  447. }
  448. [[AppGroup userDefaults] setInteger:0 forKey:@"FailedCodeAttempts"];
  449. [[AppGroup userDefaults] synchronize];
  450. [self dismissViewControllerAnimated:YES completion:nil];
  451. } else {
  452. [self lsv_unlockScreenFailure];
  453. }
  454. } else if (_lockScreenMode == LockScreenModeNormal || _lockScreenMode == LockScreenModeExtension) {
  455. // [일반 모드]
  456. _pincodeView.maxPincodeLength = [self is4DigitCodeSet] ? oldMaxLength : newMaxLength;
  457. if ([self lsv_isPincodeValid:pincode]) {
  458. [[KKPasscodeLock sharedLock] updateLastUnlockTime];
  459. if ([_delegate respondsToSelector:@selector(didPasscodeEnteredCorrectly:)]) {
  460. [_delegate performSelector:@selector(didPasscodeEnteredCorrectly:) withObject:self];
  461. }
  462. [[AppGroup userDefaults] setInteger:0 forKey:@"FailedCodeAttempts"];
  463. [[AppGroup userDefaults] synchronize];
  464. [self lsv_unlockScreenSuccessful:pincode];
  465. }
  466. else {
  467. [self lsv_unlockScreenFailure];
  468. }
  469. } else if (_lockScreenMode == LockScreenModeVerification) {
  470. _pincodeView.maxPincodeLength = [self is4DigitCodeSet] ? oldMaxLength : newMaxLength;
  471. _sameAsOldPassword = NO;
  472. if ([self lsv_isPincodeValid:pincode]) {
  473. [self setLockScreenMode:_prevLockScreenMode];
  474. if ([KKKeychain setString:pincode 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 lsv_unlockScreenSuccessful:pincode];
  481. }
  482. else {
  483. [self setLockScreenMode:_prevLockScreenMode];
  484. [self lsv_unlockScreenFailure];
  485. }
  486. } else if (_lockScreenMode == LockScreenModeChangeCheck) {
  487. _pincodeView.maxPincodeLength = newMaxLength;
  488. if ([self lsv_isPincodeValid:pincode]) {
  489. [self setLockScreenMode:LockScreenModeChange];
  490. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Enter your new passcode", @"")];
  491. [self lsv_updateSubtitle:nil];
  492. [self lsv_swipeSubtitleAndPincodeView];
  493. _threemaLogo.userInteractionEnabled = NO;
  494. [_textField resignFirstResponder];
  495. }
  496. else {
  497. [self lsv_unlockScreenFailure];
  498. }
  499. } else {
  500. _pincodeView.maxPincodeLength = newMaxLength;
  501. if (![self lsv_isPincodeValid:pincode] && _passcodeLockOn) {
  502. _sameAsOldPassword = YES;
  503. [self lsv_unlockScreenFailure];
  504. } else {
  505. _sameAsOldPassword = NO;
  506. _confirmPincode = pincode;
  507. _prevLockScreenMode = _lockScreenMode;
  508. [self setLockScreenMode:LockScreenModeVerification];
  509. // 재입력 타이틀로 전환
  510. [self lsv_updateTitle:KKPasscodeLockLocalizedString(@"Re-enter your new passcode", @"")];
  511. [self lsv_updateSubtitle:nil];
  512. _threemaLogo.userInteractionEnabled = NO;
  513. [_textField resignFirstResponder];
  514. // 서브타이틀과 pincodeviw 이동 애니메이션
  515. [self lsv_swipeSubtitleAndPincodeView];
  516. }
  517. }
  518. //------------------ Threema edit end ---------------------------
  519. }
  520. #pragma mark -
  521. #pragma mark LockScreenViewController Orientation
  522. //------------------ Threema edit begin ---------------------------
  523. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  524. if (SYSTEM_IS_IPAD) {
  525. return UIInterfaceOrientationMaskAll;
  526. }
  527. return UIInterfaceOrientationMaskAllButUpsideDown;
  528. }
  529. - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
  530. return UIInterfaceOrientationPortrait;
  531. }
  532. - (BOOL)shouldAutorotate {
  533. if (SYSTEM_IS_IPAD) {
  534. return YES;
  535. }
  536. return YES;
  537. }
  538. //------------------ Threema edit end ---------------------------
  539. //------------------ Threema edit begin ---------------------------
  540. #pragma mark -
  541. #pragma mark Threema functions
  542. - (void)eraseDataButtonPressed {
  543. NSString *title = KKPasscodeLockLocalizedString(@"Erase all data and reset passcode", @"");
  544. NSString *cancel = NSLocalizedString(@"cancel", nil);
  545. NSString *erase = KKPasscodeLockLocalizedString(@"Erase Data", @"");
  546. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert];
  547. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancel style:UIAlertActionStyleCancel handler:nil];
  548. UIAlertAction *eraseAction = [UIAlertAction actionWithTitle:erase style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
  549. [self eraseData];
  550. }];
  551. [alertController addAction:cancelAction];
  552. [alertController addAction:eraseAction];
  553. [self presentViewController:alertController animated:YES completion:nil];
  554. }
  555. - (void)eraseData {
  556. if ([_delegate respondsToSelector:@selector(shouldEraseApplicationData:)]) {
  557. [_delegate shouldEraseApplicationData:self];
  558. }
  559. }
  560. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  561. NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];
  562. [_pincodeView appendingPincode:newText];
  563. return NO;
  564. }
  565. - (BOOL)is4DigitCodeSet {
  566. NSString *passcode = [KKKeychain getStringForKey:@"passcode"];
  567. return passcode.length == oldMaxLength;
  568. }
  569. //------------------ Threema edit end ---------------------------
  570. - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
  571. for (JKLLockScreenNumber * number in _numberButtons) {
  572. [number setNeedsDisplay];
  573. }
  574. [self.pincodeView setNeedsDisplay];
  575. }
  576. @end