KKPasscodeSettingsViewController.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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 "KKPasscodeSettingsViewController.h"
  18. #import "KKKeychain.h"
  19. #import "KKPasscodeViewController.h"
  20. #import "KKPasscodeLock.h"
  21. #import "KKPasscodeGracePeriodViewController.h"
  22. #import "ModalNavigationController.h"
  23. #import "BundleUtil.h"
  24. #import "ThreemaFramework/ThreemaFramework-swift.h"
  25. #import <LocalAuthentication/LocalAuthentication.h>
  26. @implementation KKPasscodeSettingsViewController
  27. @synthesize delegate = _delegate;
  28. #pragma mark -
  29. #pragma mark UIViewController methods
  30. - (void)viewDidLoad
  31. {
  32. [super viewDidLoad];
  33. self.navigationItem.title = KKPasscodeLockLocalizedString(@"Passcode Lock", @"");
  34. _eraseDataSwitch = [[UISwitch alloc] init];
  35. [_eraseDataSwitch addTarget:self action:@selector(eraseDataSwitchChanged:) forControlEvents:UIControlEventValueChanged];
  36. _touchIdSwitch = [[UISwitch alloc] init];
  37. [_touchIdSwitch addTarget:self action:@selector(touchIdSwitchChanged:) forControlEvents:UIControlEventValueChanged];
  38. if (@available(iOS 11.0, *)) {
  39. self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
  40. }
  41. }
  42. - (void)viewDidUnload
  43. {
  44. _eraseDataSwitch = nil;
  45. _touchIdSwitch = nil;
  46. [super viewDidUnload];
  47. }
  48. - (void)viewWillAppear:(BOOL)animated
  49. {
  50. [super viewWillAppear:animated];
  51. [self readSettings];
  52. [self.tableView reloadData];
  53. }
  54. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  55. if (SYSTEM_IS_IPAD) {
  56. return UIInterfaceOrientationMaskAll;
  57. }
  58. return UIInterfaceOrientationMaskAllButUpsideDown;
  59. }
  60. - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
  61. return UIInterfaceOrientationPortrait;
  62. }
  63. - (BOOL)shouldAutorotate {
  64. if (SYSTEM_IS_IPAD) {
  65. return YES;
  66. }
  67. return YES;
  68. }
  69. - (void)eraseOn {
  70. _eraseDataOn = YES;
  71. [KKKeychain setString:@"YES" forKey:@"erase_data_on"];
  72. [_eraseDataSwitch setOn:_eraseDataOn animated:YES];
  73. }
  74. - (void)eraseOff {
  75. _eraseDataOn = NO;
  76. [KKKeychain setString:@"NO" forKey:@"erase_data_on"];
  77. [_eraseDataSwitch setOn:_eraseDataOn animated:YES];
  78. }
  79. - (void)eraseDataSwitchChanged:(id)sender
  80. {
  81. if (_eraseDataSwitch.on) {
  82. NSString* title = [NSString stringWithFormat:KKPasscodeLockLocalizedString(@"All data in this app will be erased after %d failed passcode attempts.", @""), [[KKPasscodeLock sharedLock] attemptsAllowed]];
  83. NSString* cancel = KKPasscodeLockLocalizedString(@"Cancel", @"");
  84. NSString* erase = KKPasscodeLockLocalizedString(@"Enable", @"");
  85. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:@"" preferredStyle:UIAlertControllerStyleAlert];
  86. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancel style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
  87. [self eraseOff];
  88. }];
  89. UIAlertAction *enableAction = [UIAlertAction actionWithTitle:erase style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
  90. [self eraseOn];
  91. }];
  92. [alertController addAction:cancelAction];
  93. [alertController addAction:enableAction];
  94. [self presentViewController:alertController animated:YES completion:nil];
  95. } else {
  96. _eraseDataOn = NO;
  97. [KKKeychain setString:@"NO" forKey:@"erase_data_on"];
  98. }
  99. }
  100. - (void)touchIdSwitchChanged:(id)sender
  101. {
  102. if (_touchIdSwitch.on) {
  103. _touchIdOn = YES;
  104. [KKKeychain setString:@"YES" forKey:@"touch_id_on"];
  105. } else {
  106. _touchIdOn = NO;
  107. [KKKeychain setString:@"NO" forKey:@"touch_id_on"];
  108. }
  109. }
  110. - (NSString *)touchIdType {
  111. LAContext *context = [LAContext new];
  112. if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]) {
  113. BOOL haveFaceId = NO;
  114. if (@available(iOS 11.0, *)) {
  115. if (context.biometryType == LABiometryTypeFaceID) {
  116. haveFaceId = YES;
  117. }
  118. }
  119. if (haveFaceId) {
  120. return @"Face ID";
  121. } else {
  122. return @"Touch ID";
  123. }
  124. } else {
  125. return nil;
  126. }
  127. }
  128. #pragma mark -
  129. #pragma mark UITableViewDataSource methods
  130. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  131. {
  132. if ([[KKPasscodeLock sharedLock] eraseOption]) {
  133. return 4;
  134. }
  135. return 3;
  136. }
  137. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  138. {
  139. if (section == 0)
  140. return 2;
  141. else
  142. return 1;
  143. }
  144. - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
  145. {
  146. if (section == 0) {
  147. return KKPasscodeLockLocalizedString(@"The app passcode lock protects your data against prying eyes, but it is not a replacement for setting a passcode lock on your device.\nIf you haven't done so already, set a device passcode lock too in order to ensure that the data on your device is encrypted.", @"");
  148. }
  149. else if (section == 3) {
  150. return [NSString stringWithFormat:KKPasscodeLockLocalizedString(@"Erase all content in the app after %d failed passcode attempts.", @""), [[KKPasscodeLock sharedLock] attemptsAllowed]];;
  151. } else {
  152. return @"";
  153. }
  154. }
  155. - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
  156. {
  157. static NSString *CellIdentifier = @"KKPasscodeSettingsCell";
  158. static NSString *CellIdentifier2 = @"KKPasscodeSettingsCell2";
  159. UITableViewCell *cell;
  160. if (indexPath.section == 1) {
  161. cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
  162. if (cell == nil)
  163. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier2];
  164. } else {
  165. cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  166. if (cell == nil)
  167. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
  168. }
  169. cell.accessoryView = nil;
  170. cell.selectionStyle = UITableViewCellSelectionStyleBlue;
  171. #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
  172. cell.textLabel.textAlignment = UITextAlignmentLeft;
  173. #else
  174. cell.textLabel.textAlignment = NSTextAlignmentLeft;
  175. #endif
  176. cell.textLabel.textColor = [UIColor blackColor];
  177. if (indexPath.section == 0) {
  178. #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
  179. cell.textLabel.textAlignment = UITextAlignmentCenter;
  180. #else
  181. cell.textLabel.textAlignment = NSTextAlignmentCenter;
  182. #endif
  183. cell.textLabel.textColor = [UIColor colorWithRed:0 green:122.0f/255.0f blue:1.0f alpha:1.0f];
  184. if (indexPath.row == 0) {
  185. if (_passcodeLockOn) {
  186. cell.textLabel.text = KKPasscodeLockLocalizedString(@"Turn Passcode Off", @"");
  187. } else {
  188. cell.textLabel.text = KKPasscodeLockLocalizedString(@"Turn Passcode On", @"");
  189. }
  190. cell.accessibilityTraits = UIAccessibilityTraitButton;
  191. } else {
  192. cell.textLabel.text = KKPasscodeLockLocalizedString(@"Change Passcode", @"");
  193. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  194. cell.accessibilityTraits = UIAccessibilityTraitButton;
  195. if (!_passcodeLockOn) {
  196. cell.accessibilityTraits = UIAccessibilityTraitNotEnabled;
  197. cell.textLabel.textColor = [UIColor grayColor];
  198. }
  199. }
  200. } else if (indexPath.section == 1) {
  201. cell.textLabel.text = KKPasscodeLockLocalizedString(@"Require Passcode", @"");
  202. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  203. cell.detailTextLabel.text = [KKPasscodeSettingsViewController textForGracePeriod:_gracePeriod shortForm:YES];
  204. } else if (indexPath.section == 2) {
  205. BOOL touchIdSupported = YES;
  206. NSString *touchIdType = [self touchIdType];
  207. if (touchIdType == nil) {
  208. touchIdSupported = NO;
  209. touchIdType = @"Touch ID"; // for grayed out label
  210. }
  211. cell.accessoryView = _touchIdSwitch;
  212. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  213. cell.textLabel.text = touchIdType;
  214. if (_passcodeLockOn && touchIdSupported) {
  215. cell.textLabel.textColor = [UIColor blackColor];
  216. _touchIdSwitch.enabled = YES;
  217. cell.accessibilityTraits = UIAccessibilityTraitNone;
  218. } else {
  219. cell.textLabel.textColor = [UIColor grayColor];
  220. _touchIdSwitch.enabled = NO;
  221. cell.accessibilityTraits = UIAccessibilityTraitNotEnabled;
  222. }
  223. } else if (indexPath.section == 3) {
  224. cell.textLabel.text = KKPasscodeLockLocalizedString(@"Erase Data", @"");
  225. cell.accessoryView = _eraseDataSwitch;
  226. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  227. if (_passcodeLockOn) {
  228. cell.textLabel.textColor = [UIColor blackColor];
  229. _eraseDataSwitch.enabled = YES;
  230. cell.accessibilityTraits = UIAccessibilityTraitNone;
  231. } else {
  232. cell.textLabel.textColor = [UIColor grayColor];
  233. _eraseDataSwitch.enabled = NO;
  234. cell.accessibilityTraits = UIAccessibilityTraitNotEnabled;
  235. }
  236. }
  237. return cell;
  238. }
  239. #pragma mark - UITableViewDelegate methods
  240. - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
  241. {
  242. if (indexPath.section == 0 && indexPath.row == 0) {
  243. if (!_passcodeLockOn) {
  244. NSString *title = KKPasscodeLockLocalizedString(@"Passcode lock notice", @"");
  245. NSString *message = KKPasscodeLockLocalizedString(@"ResetInfo", @"");
  246. NSString *cancel = NSLocalizedString(@"cancel", nil);
  247. NSString *ok = NSLocalizedString(@"ok", nil);
  248. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
  249. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancel style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
  250. [self cancelAction];
  251. }];
  252. UIAlertAction *okAction = [UIAlertAction actionWithTitle:ok style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
  253. [self okAction];
  254. }];
  255. [alertController addAction:cancelAction];
  256. [alertController addAction:okAction];
  257. [self presentViewController:alertController animated:YES completion:nil];
  258. } else {
  259. [self showPasswordViewControllerWithMode:KKPasscodeModeDisabled];
  260. }
  261. } else if (indexPath.section == 0 && indexPath.row == 1 && _passcodeLockOn) {
  262. [self showPasswordViewControllerWithMode:KKPasscodeModeChange];
  263. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  264. } else if (indexPath.section == 1) {
  265. KKPasscodeGracePeriodViewController *gvc = [[KKPasscodeGracePeriodViewController alloc] initWithStyle:UITableViewStyleGrouped];
  266. [self.navigationController pushViewController:gvc animated:YES];
  267. }
  268. }
  269. - (void)didSettingsChanged:(KKPasscodeViewController*)viewController
  270. {
  271. [self readSettings];
  272. [self.tableView reloadData];
  273. if ([_delegate respondsToSelector:@selector(didSettingsChanged:)]) {
  274. [_delegate performSelector:@selector(didSettingsChanged:) withObject:self];
  275. }
  276. }
  277. - (void)readSettings {
  278. _passcodeLockOn = [[KKKeychain getStringForKey:@"passcode_on"] isEqualToString:@"YES"];
  279. _eraseDataOn = [[KKKeychain getStringForKey:@"erase_data_on"] isEqualToString:@"YES"];
  280. _eraseDataSwitch.on = _eraseDataOn;
  281. _gracePeriod = [[KKKeychain getStringForKey:@"grace_period"] intValue];
  282. _touchIdOn = [[KKKeychain getStringForKey:@"touch_id_on"] isEqualToString:@"YES"];
  283. _touchIdSwitch.on = _touchIdOn;
  284. }
  285. - (void)showPasswordViewControllerWithMode:(KKPasscodeMode)mode {
  286. JKLLockScreenViewController *vc = [[JKLLockScreenViewController alloc] initWithNibName:NSStringFromClass([JKLLockScreenViewController class]) bundle:[BundleUtil frameworkBundle]];
  287. switch (mode) {
  288. case KKPasscodeModeEnter:
  289. vc.lockScreenMode = LockScreenModeNormal;
  290. break;
  291. case KKPasscodeModeSet:
  292. vc.lockScreenMode = LockScreenModeNew;
  293. break;
  294. case KKPasscodeModeDisabled:
  295. vc.lockScreenMode = LockScreenModeDisable;
  296. break;
  297. case KKPasscodeModeChange:
  298. vc.lockScreenMode = LockScreenModeChangeCheck;
  299. break;
  300. default:
  301. break;
  302. }
  303. vc.delegate = self;
  304. UINavigationController *nav = [[ModalNavigationController alloc] initWithRootViewController:vc];
  305. nav.navigationBarHidden = YES;
  306. nav.modalPresentationStyle = UIModalPresentationFullScreen;
  307. [self.navigationController presentViewController:nav animated:YES completion:nil];
  308. }
  309. - (void)cancelAction {
  310. [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
  311. }
  312. - (void)okAction {
  313. [self showPasswordViewControllerWithMode:KKPasscodeModeSet];
  314. }
  315. + (NSString*)textForGracePeriod:(int)gracePeriod shortForm:(BOOL)shortForm {
  316. if (gracePeriod == 0) {
  317. return KKPasscodeLockLocalizedString(@"Immediately", @"");
  318. } else if (gracePeriod == 60) {
  319. if (shortForm)
  320. return KKPasscodeLockLocalizedString(@"After 1 min.", @"");
  321. else
  322. return KKPasscodeLockLocalizedString(@"After 1 minute", @"");
  323. } else if (gracePeriod < 3600) {
  324. if (shortForm)
  325. return [NSString stringWithFormat:KKPasscodeLockLocalizedString(@"After %d min.", @""), gracePeriod / 60];
  326. else
  327. return [NSString stringWithFormat:KKPasscodeLockLocalizedString(@"After %d minutes", @""), gracePeriod / 60];
  328. } else if (gracePeriod == 3600) {
  329. return KKPasscodeLockLocalizedString(@"After 1 hour", @"");
  330. } else {
  331. return [NSString stringWithFormat:KKPasscodeLockLocalizedString(@"After %d hours", @""), gracePeriod / 3600];
  332. }
  333. }
  334. @end