// // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import "FingerprintViewScanController.h" #import "Environment.h" #import "OWSContactsManager.h" #import "OWSQRCodeScanningViewController.h" #import "Signal-Swift.h" #import "UIColor+OWS.h" #import "UIFont+OWS.h" #import "UIUtil.h" #import "UIView+OWS.h" #import "UIViewController+CameraPermissions.h" #import #import #import NS_ASSUME_NONNULL_BEGIN @interface FingerprintViewScanController () @property (nonatomic) TSStorageManager *storageManager; @property (nonatomic) OWSFingerprint *fingerprint; @property (nonatomic) NSString *contactName; @property (nonatomic) OWSQRCodeScanningViewController *qrScanningController; @end @implementation FingerprintViewScanController - (void)configureWithRecipientId:(NSString *)recipientId { OWSAssert(recipientId.length > 0); self.storageManager = [TSStorageManager sharedManager]; OWSContactsManager *contactsManager = [Environment getCurrent].contactsManager; self.contactName = [contactsManager displayNameForPhoneIdentifier:recipientId]; OWSRecipientIdentity *_Nullable recipientIdentity = [[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId]; OWSAssert(recipientIdentity); OWSFingerprintBuilder *builder = [[OWSFingerprintBuilder alloc] initWithStorageManager:self.storageManager contactsManager:contactsManager]; self.fingerprint = [builder fingerprintWithTheirSignalId:recipientId theirIdentityKey:recipientIdentity.identityKey]; } - (void)loadView { [super loadView]; self.title = NSLocalizedString(@"SCAN_QR_CODE_VIEW_TITLE", @"Title for the 'scan QR code' view."); [self createViews]; } - (void)createViews { UIColor *darkGrey = [UIColor colorWithRGBHex:0x404040]; self.view.backgroundColor = [UIColor blackColor]; self.qrScanningController = [OWSQRCodeScanningViewController new]; self.qrScanningController.scanDelegate = self; [self.view addSubview:self.qrScanningController.view]; [self.qrScanningController.view autoPinWidthToSuperview]; [self.qrScanningController.view autoPinToTopLayoutGuideOfViewController:self withInset:0]; UIView *footer = [UIView new]; footer.backgroundColor = darkGrey; [self.view addSubview:footer]; [footer autoPinWidthToSuperview]; [footer autoPinToBottomLayoutGuideOfViewController:self withInset:0]; [footer autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.qrScanningController.view]; UILabel *cameraInstructionLabel = [UILabel new]; cameraInstructionLabel.text = NSLocalizedString(@"SCAN_CODE_INSTRUCTIONS", @"label presented once scanning (camera) view is visible."); cameraInstructionLabel.font = [UIFont ows_regularFontWithSize:14.f]; cameraInstructionLabel.textColor = [UIColor whiteColor]; cameraInstructionLabel.textAlignment = NSTextAlignmentCenter; cameraInstructionLabel.numberOfLines = 0; cameraInstructionLabel.lineBreakMode = NSLineBreakByWordWrapping; [footer addSubview:cameraInstructionLabel]; [cameraInstructionLabel autoPinWidthToSuperviewWithMargin:16.f]; [cameraInstructionLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:10.f]; [cameraInstructionLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:10.f]; } - (void)viewWillAppear:(BOOL)animated { // In case we're returning from activity view that needed default system styles. [UIUtil applySignalAppearence]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:YES]; } #pragma mark - Action - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self ows_askForCameraPermissions:^{ // Camera stops capturing when "sharing" while in capture mode. // Also, it's less obvious whats being "shared" at this point, // so just disable sharing when in capture mode. DDLogInfo(@"%@ Showing Scanner", self.tag); [self.qrScanningController startCapture]; } failureCallback:^{ [self.navigationController popViewControllerAnimated:YES]; }]; } #pragma mark - OWSQRScannerDelegate - (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data { [self verifyCombinedFingerprintData:data]; } - (void)verifyCombinedFingerprintData:(NSData *)combinedFingerprintData { NSError *error; if ([self.fingerprint matchesLogicalFingerprintsData:combinedFingerprintData error:&error]) { [self showVerificationSucceeded]; } else { [self showVerificationFailedWithError:error]; } } - (void)showVerificationSucceeded { DDLogInfo(@"%@ Successfully verified privacy.", self.tag); NSString *successTitle = NSLocalizedString(@"SUCCESSFUL_VERIFICATION_TITLE", nil); NSString *dismissText = NSLocalizedString(@"DISMISS_BUTTON_TEXT", nil); NSString *descriptionFormat = NSLocalizedString( @"SUCCESSFUL_VERIFICATION_DESCRIPTION", @"Alert body after verifying privacy with {{other user's name}}"); NSString *successDescription = [NSString stringWithFormat:descriptionFormat, self.contactName]; UIAlertController *successAlertController = [UIAlertController alertControllerWithTitle:successTitle message:successDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:dismissText style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [self dismissViewControllerAnimated:true completion:nil]; }]; [successAlertController addAction:dismissAction]; [self presentViewController:successAlertController animated:YES completion:nil]; } - (void)showVerificationFailedWithError:(NSError *)error { NSString *_Nullable failureTitle; if (error.code != OWSErrorCodeUserError) { failureTitle = NSLocalizedString(@"FAILED_VERIFICATION_TITLE", @"alert title"); } // else no title. We don't want to show a big scary "VERIFICATION FAILED" when it's just user error. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:failureTitle message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"RETRY_BUTTON_TEXT", @"Generic text for button that retries whatever the last action was.") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { [self.qrScanningController startCapture]; }]]; UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { [self.navigationController popViewControllerAnimated:YES]; }]; [alertController addAction:dismissAction]; [self presentViewController:alertController animated:YES completion:nil]; DDLogWarn(@"%@ Identity verification failed with error: %@", self.tag, error); } - (void)dismissViewControllerAnimated:(BOOL)animated completion:(nullable void (^)(void))completion { self.qrScanningController.view.hidden = YES; [super dismissViewControllerAnimated:animated completion:completion]; } #pragma mark - Logging + (NSString *)tag { return [NSString stringWithFormat:@"[%@]", self.class]; } - (NSString *)tag { return self.class.tag; } @end NS_ASSUME_NONNULL_END