From 58ebebc97f558d2f9ffb27118c54491da65de430 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 8 Jun 2017 18:10:02 -0400 Subject: [PATCH 1/4] Move QR code scanning to a separate view. // FREEBIE --- Signal.xcodeproj/project.pbxproj | 6 + .../ConversationView/MessagesViewController.m | 3 +- .../FingerprintViewController.h | 11 +- .../FingerprintViewController.m | 239 +++--------------- .../FingerprintViewScanController.h | 13 + .../FingerprintViewScanController.m | 218 ++++++++++++++++ ...SConversationSettingsTableViewController.h | 1 - ...SConversationSettingsTableViewController.m | 7 - .../OWSLinkedDevicesTableViewController.m | 9 +- .../OWSQRCodeScanningViewController.h | 7 +- .../OWSQRCodeScanningViewController.m | 86 +++---- .../util/UIViewController+CameraPermissions.h | 10 +- .../util/UIViewController+CameraPermissions.m | 40 ++- .../translations/en.lproj/Localizable.strings | 3 + 14 files changed, 349 insertions(+), 304 deletions(-) create mode 100644 Signal/src/ViewControllers/FingerprintViewScanController.h create mode 100644 Signal/src/ViewControllers/FingerprintViewScanController.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index ce211210d..b7543a402 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -80,6 +80,7 @@ 34D8C02B1ED3685800188D7C /* DebugUIContacts.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C02A1ED3685800188D7C /* DebugUIContacts.m */; }; 34DFCB851E8E04B500053165 /* AddToBlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */; }; 34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; }; + 34E8BF381EE9E2FD00F5F4CA /* FingerprintViewScanController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E8BF371EE9E2FD00F5F4CA /* FingerprintViewScanController.m */; }; 34F3089C1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F3089B1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m */; }; 34F3089F1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F3089E1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m */; }; 34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; }; @@ -491,6 +492,8 @@ 34DFCB831E8E04B400053165 /* AddToBlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToBlockListViewController.h; sourceTree = ""; }; 34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToBlockListViewController.m; sourceTree = ""; }; 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProgressView.swift; sourceTree = ""; }; + 34E8BF361EE9E2FD00F5F4CA /* FingerprintViewScanController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FingerprintViewScanController.h; sourceTree = ""; }; + 34E8BF371EE9E2FD00F5F4CA /* FingerprintViewScanController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FingerprintViewScanController.m; sourceTree = ""; }; 34F3089A1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSUnreadIndicatorInteraction.h; sourceTree = ""; }; 34F3089B1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSUnreadIndicatorInteraction.m; sourceTree = ""; }; 34F3089D1ECA580B00BB7697 /* OWSUnreadIndicatorCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUnreadIndicatorCell.h; sourceTree = ""; }; @@ -927,6 +930,8 @@ 34B3F8441E8DF1700035BE1A /* ExperienceUpgradesPageViewController.swift */, 34B3F8451E8DF1700035BE1A /* FingerprintViewController.h */, 34B3F8461E8DF1700035BE1A /* FingerprintViewController.m */, + 34E8BF361EE9E2FD00F5F4CA /* FingerprintViewScanController.h */, + 34E8BF371EE9E2FD00F5F4CA /* FingerprintViewScanController.m */, 34B3F8471E8DF1700035BE1A /* FullImageViewController.h */, 34B3F8481E8DF1700035BE1A /* FullImageViewController.m */, 34D5CCA71EAE3D30005515DB /* GroupViewHelper.h */, @@ -2209,6 +2214,7 @@ 45666F7E1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m in Sources */, 4579431E1E7C8CE9008ED0C0 /* Pastelog.m in Sources */, 34B3F8941E8DF1710035BE1A /* SignalsViewController.m in Sources */, + 34E8BF381EE9E2FD00F5F4CA /* FingerprintViewScanController.m in Sources */, 76EB058818170B33006006FC /* PropertyListPreferences.m in Sources */, 34330A611E788EA900DF2FB9 /* AttachmentUploadView.m in Sources */, 34B3F87D1E8DF1700035BE1A /* FullImageViewController.m in Sources */, diff --git a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m index efb399ac4..012aacb8f 100644 --- a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m +++ b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m @@ -2729,8 +2729,7 @@ typedef enum : NSUInteger { dispatch_async(dispatch_get_main_queue(), ^{ [self presentViewController:picker animated:YES completion:[UIUtil modalCompletionBlock]]; }); - } - alertActionHandler:nil]; + }]; } - (void)chooseFromLibrary { diff --git a/Signal/src/ViewControllers/FingerprintViewController.h b/Signal/src/ViewControllers/FingerprintViewController.h index ef8ab4efe..c9c2a2374 100644 --- a/Signal/src/ViewControllers/FingerprintViewController.h +++ b/Signal/src/ViewControllers/FingerprintViewController.h @@ -2,21 +2,12 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // -#import "OWSQRCodeScanningViewController.h" - NS_ASSUME_NONNULL_BEGIN -@class OWSFingerprint; -@class OWSConversationSettingsTableViewController; - -@interface FingerprintViewController : UIViewController - -@property (nullable) OWSConversationSettingsTableViewController *dismissDelegate; +@interface FingerprintViewController : UIViewController - (void)configureWithRecipientId:(NSString *)recipientId NS_SWIFT_NAME(configure(recipientId:)); -- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data; - @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/FingerprintViewController.m b/Signal/src/ViewControllers/FingerprintViewController.m index 11d25d73d..519d5da65 100644 --- a/Signal/src/ViewControllers/FingerprintViewController.m +++ b/Signal/src/ViewControllers/FingerprintViewController.m @@ -4,12 +4,14 @@ #import "FingerprintViewController.h" #import "Environment.h" +#import "FingerprintViewScanController.h" #import "OWSBezierPathView.h" -#import "OWSConversationSettingsTableViewController.h" -#import "OWSQRCodeScanningViewController.h" +#import "OWSContactsManager.h" #import "Signal-Swift.h" +#import "UIColor+OWS.h" +#import "UIFont+OWS.h" #import "UIUtil.h" -#import "UIViewController+CameraPermissions.h" +#import "UIView+OWS.h" #import #import #import @@ -44,18 +46,12 @@ typedef void (^CustomLayoutBlock)(); @interface FingerprintViewController () +@property (nonatomic) NSString *recipientId; @property (nonatomic) TSStorageManager *storageManager; @property (nonatomic) OWSFingerprint *fingerprint; @property (nonatomic) NSString *contactName; -@property (nonatomic) OWSQRCodeScanningViewController *qrScanningController; @property (nonatomic) UIBarButtonItem *shareButton; -@property (nonatomic) UIView *mainView; -@property (nonatomic) UIView *referenceView; -@property (nonatomic) UIView *cameraView; - -@property (nonatomic) NSLayoutConstraint *verticalAlignmentConstraint; -@property (nonatomic) BOOL isScanning; @end @@ -65,6 +61,8 @@ typedef void (^CustomLayoutBlock)(); { OWSAssert(recipientId.length > 0); + self.recipientId = recipientId; + self.storageManager = [TSStorageManager sharedManager]; OWSContactsManager *contactsManager = [Environment getCurrent].contactsManager; @@ -104,59 +102,12 @@ typedef void (^CustomLayoutBlock)(); self.view.backgroundColor = [UIColor whiteColor]; - UIView *referenceView = [UIView new]; - self.referenceView = referenceView; - [self.view addSubview:referenceView]; - [referenceView autoPinWidthToSuperview]; - [referenceView autoPinToTopLayoutGuideOfViewController:self withInset:0]; - [referenceView autoPinToBottomLayoutGuideOfViewController:self withInset:0]; - UIView *mainView = [UIView new]; - self.mainView = mainView; mainView.backgroundColor = [UIColor whiteColor]; [self.view addSubview:mainView]; [mainView autoPinWidthToSuperview]; - [[NSLayoutConstraint constraintWithItem:mainView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:referenceView - attribute:NSLayoutAttributeHeight - multiplier:1.0 - constant:0.f] autoInstall]; - [mainView - addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(mainViewTapped:)]]; - mainView.userInteractionEnabled = YES; - - UIView *cameraView = [UIView new]; - self.cameraView = cameraView; - cameraView.backgroundColor = [UIColor whiteColor]; - [self.view addSubview:cameraView]; - [cameraView autoPinWidthToSuperview]; - [cameraView autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:mainView]; - [cameraView autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:mainView]; - - self.qrScanningController = [OWSQRCodeScanningViewController new]; - self.qrScanningController.scanDelegate = self; - [cameraView addSubview:self.qrScanningController.view]; - [self.qrScanningController.view autoPinWidthToSuperview]; - [self.qrScanningController.view autoSetDimension:ALDimensionHeight toSize:270]; - [self.qrScanningController.view autoPinEdgeToSuperviewEdge:ALEdgeTop]; - - 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 = darkGrey; - cameraInstructionLabel.textAlignment = NSTextAlignmentCenter; - cameraInstructionLabel.numberOfLines = 0; - cameraInstructionLabel.lineBreakMode = NSLineBreakByWordWrapping; - [cameraView addSubview:cameraInstructionLabel]; - [cameraInstructionLabel autoPinWidthToSuperviewWithMargin:16.f]; - [cameraInstructionLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:10.f]; - [cameraInstructionLabel autoPinEdge:ALEdgeTop - toEdge:ALEdgeBottom - ofView:self.qrScanningController.view - withOffset:10.f]; + [mainView autoPinToTopLayoutGuideOfViewController:self withInset:0]; + [mainView autoPinToBottomLayoutGuideOfViewController:self withInset:0]; // Scan Button UIView *scanButton = [UIView new]; @@ -249,65 +200,6 @@ typedef void (^CustomLayoutBlock)(); fingerprintImageView.frame = CGRectMake( round((fingerprintView.width - size) * 0.5f), round((fingerprintView.height - size) * 0.5f), size, size); }; - - [self updateLayoutForIsScanning:NO animated:NO]; -} - -- (void)showScanningViews -{ - [self updateLayoutForIsScanning:YES animated:YES]; -} - -- (void)hideScanningViews -{ - [self updateLayoutForIsScanning:NO animated:YES]; -} - -- (void)updateLayoutForIsScanning:(BOOL)isScanning animated:(BOOL)animated -{ - self.isScanning = isScanning; - - if (self.verticalAlignmentConstraint) { - [NSLayoutConstraint deactivateConstraints:@[ self.verticalAlignmentConstraint ]]; - } - if (isScanning) { - self.verticalAlignmentConstraint = - [self.cameraView autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:self.referenceView]; - } else { - self.verticalAlignmentConstraint = - [self.mainView autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:self.referenceView]; - } - [self.view setNeedsLayout]; - - // Show scanning views immediately. - if (isScanning) { - self.shareButton.enabled = NO; - self.cameraView.hidden = NO; - } - - void (^completion)() = ^{ - if (!isScanning) { - // Hide scanning views after they are offscreen. - self.shareButton.enabled = YES; - self.cameraView.hidden = YES; - } - }; - - if (animated) { - [UIView animateWithDuration:0.4 - delay:0.0 - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - [self.view layoutSubviews]; - } - completion:^(BOOL finished) { - if (finished) { - completion(); - } - }]; - } else { - completion(); - } } - (void)viewWillAppear:(BOOL)animated @@ -316,15 +208,7 @@ typedef void (^CustomLayoutBlock)(); [UIUtil applySignalAppearence]; } -- (void)viewWillDisappear:(BOOL)animated -{ - [super viewWillDisappear:YES]; - if (self.dismissDelegate) { - [self.dismissDelegate presentedModalWasDismissed]; - } -} - -#pragma mark - HilightableLableDelegate +#pragma mark - - (void)showSharingActivityWithCompletion:(nullable void (^)(void))completionHandler { @@ -372,52 +256,6 @@ typedef void (^CustomLayoutBlock)(); [self showVerificationFailedWithError:error]; } -#pragma mark - Action - -- (void)closeButton -{ - [self dismissViewControllerAnimated:YES completion:nil]; -} - -- (void)didTapShareButton -{ - [self showSharingActivityWithCompletion:nil]; -} - -- (void)showScanner -{ - [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 showScanningViews]; - - [self.qrScanningController startCapture]; - } - alertActionHandler:nil]; -} - -#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); @@ -430,10 +268,11 @@ typedef void (^CustomLayoutBlock)(); [UIAlertController alertControllerWithTitle:successTitle message:successDescription preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *dismissAction = - [UIAlertAction actionWithTitle:dismissText style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){ - [self dismissViewControllerAnimated:true completion:nil]; - }]; + 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]; @@ -453,28 +292,31 @@ typedef void (^CustomLayoutBlock)(); NSString *dismissText = NSLocalizedString(@"DISMISS_BUTTON_TEXT", nil); UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:dismissText style:UIAlertActionStyleCancel handler: ^(UIAlertAction *action){ - - // Restore previous layout - [self hideScanningViews]; }]; [failureAlertController addAction:dismissAction]; - // TODO - // NSString retryText = NSLocalizedString(@"RETRY_BUTTON_TEXT", nil); - // UIAlertAction *retryAction = [UIAlertAction actionWithTitle:retryText style:UIAlertActionStyleDefault - // handler:^(UIAlertAction * _Nonnull action) { - // - // }]; - // [failureAlertController addAction:retryAction]; [self presentViewController:failureAlertController animated:YES completion:nil]; DDLogWarn(@"%@ Identity verification failed with error: %@", self.tag, error); } -- (void)dismissViewControllerAnimated:(BOOL)flag completion:(nullable void (^)(void))completion +#pragma mark - Action + +- (void)closeButton +{ + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)didTapShareButton { - [self updateLayoutForIsScanning:NO animated:NO]; - [super dismissViewControllerAnimated:flag completion:completion]; + [self showSharingActivityWithCompletion:nil]; +} + +- (void)showScanner +{ + FingerprintViewScanController *scanView = [FingerprintViewScanController new]; + [scanView configureWithRecipientId:self.recipientId]; + [self.navigationController pushViewController:scanView animated:YES]; } - (void)scanButtonTapped:(UIGestureRecognizer *)gestureRecognizer @@ -487,10 +329,6 @@ typedef void (^CustomLayoutBlock)(); - (void)fingerprintLabelTapped:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { - if (self.isScanning) { - [self hideScanningViews]; - return; - } [self showSharingActivityWithCompletion:nil]; } } @@ -498,23 +336,10 @@ typedef void (^CustomLayoutBlock)(); - (void)fingerprintViewTapped:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { - if (self.isScanning) { - [self hideScanningViews]; - return; - } [self showSharingActivityWithCompletion:nil]; } } -- (void)mainViewTapped:(UIGestureRecognizer *)gestureRecognizer -{ - if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) { - if (self.isScanning) { - [self hideScanningViews]; - } - } -} - #pragma mark - Logging + (NSString *)tag diff --git a/Signal/src/ViewControllers/FingerprintViewScanController.h b/Signal/src/ViewControllers/FingerprintViewScanController.h new file mode 100644 index 000000000..7e5639572 --- /dev/null +++ b/Signal/src/ViewControllers/FingerprintViewScanController.h @@ -0,0 +1,13 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@interface FingerprintViewScanController : UIViewController + +- (void)configureWithRecipientId:(NSString *)recipientId NS_SWIFT_NAME(configure(recipientId:)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/FingerprintViewScanController.m b/Signal/src/ViewControllers/FingerprintViewScanController.m new file mode 100644 index 000000000..be702fd5b --- /dev/null +++ b/Signal/src/ViewControllers/FingerprintViewScanController.m @@ -0,0 +1,218 @@ +// +// 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 diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h index 6735e11fb..e94c19c99 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h @@ -15,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak) id conversationSettingsViewDelegate; - (void)configureWithThread:(TSThread *)thread; -- (void)presentedModalWasDismissed; @end diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m index 21a1761dc..a11c97ebd 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m @@ -224,7 +224,6 @@ NS_ASSUME_NONNULL_BEGIN } FingerprintViewController *fingerprintViewController = [FingerprintViewController new]; [fingerprintViewController configureWithRecipientId:strongSelf.thread.contactIdentifier]; - fingerprintViewController.dismissDelegate = strongSelf; UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:fingerprintViewController]; [strongSelf presentViewController:navigationController animated:YES completion:nil]; @@ -718,12 +717,6 @@ NS_ASSUME_NONNULL_BEGIN [self.navigationController popViewControllerAnimated:YES]; } -- (void)presentedModalWasDismissed -{ - // Else row stays selected after dismissing modal. - [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES]; -} - - (void)disappearingMessagesSwitchValueDidChange:(UISwitch *)sender { UISwitch *disappearingMessagesSwitch = (UISwitch *)sender; diff --git a/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m b/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m index ee8e25a88..e08cbf6d4 100644 --- a/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m +++ b/Signal/src/ViewControllers/OWSLinkedDevicesTableViewController.m @@ -254,16 +254,13 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1; -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES]; + if (indexPath.section == OWSLinkedDevicesTableViewControllerSectionAddDevice) { [self ows_askForCameraPermissions:^{ [self performSegueWithIdentifier:@"LinkDeviceSegue" sender:self]; - } - alertActionHandler:^{ - // HACK to unselect rows when swiping back - // http://stackoverflow.com/questions/19379510/uitableviewcell-doesnt-get-deselected-when-swiping-back-quickly - [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES]; - }]; + }]; } } diff --git a/Signal/src/ViewControllers/OWSQRCodeScanningViewController.h b/Signal/src/ViewControllers/OWSQRCodeScanningViewController.h index 515c87a2d..533badb11 100644 --- a/Signal/src/ViewControllers/OWSQRCodeScanningViewController.h +++ b/Signal/src/ViewControllers/OWSQRCodeScanningViewController.h @@ -1,4 +1,6 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import #import @@ -15,10 +17,13 @@ @end +#pragma mark - + @interface OWSQRCodeScanningViewController : UIViewController @property (nonatomic, weak) UIViewController *scanDelegate; + - (void)startCapture; @end diff --git a/Signal/src/ViewControllers/OWSQRCodeScanningViewController.m b/Signal/src/ViewControllers/OWSQRCodeScanningViewController.m index a6ab916a6..d354426bf 100644 --- a/Signal/src/ViewControllers/OWSQRCodeScanningViewController.m +++ b/Signal/src/ViewControllers/OWSQRCodeScanningViewController.m @@ -3,14 +3,15 @@ // #import "OWSQRCodeScanningViewController.h" +#import "OWSBezierPathView.h" #import "UIColor+OWS.h" +#import "UIView+OWS.h" @interface OWSQRCodeScanningViewController () +@property (atomic) ZXCapture *capture; @property (nonatomic) BOOL captureEnabled; -@property (atomic, strong) ZXCapture *capture; -@property UIView *maskingView; -@property CALayer *maskingLayer; +@property (nonatomic) UIView *maskingView; @end @@ -47,11 +48,33 @@ return self; } -- (void)viewDidLoad +- (void)loadView { - [super viewDidLoad]; - self.maskingView = [[UIView alloc] initWithFrame:self.view.frame]; - [self.view addSubview:self.maskingView]; + [super loadView]; + + OWSBezierPathView *maskingView = [OWSBezierPathView new]; + self.maskingView = maskingView; + [maskingView setConfigureShapeLayerBlock:^(CAShapeLayer *layer, CGRect bounds) { + // Add a circular mask + UIBezierPath *path = [UIBezierPath bezierPathWithRect:bounds]; + CGFloat verticalMargin = 8.0; + CGFloat radius = MIN(bounds.size.width, bounds.size.height) * 0.5f - verticalMargin; + + // Center the circle's bounding rectangle + CGRect circleRect = CGRectMake( + bounds.size.width * 0.5f - radius, bounds.size.height * 0.5f - radius, radius * 2.f, radius * 2.f); + UIBezierPath *circlePath = [UIBezierPath bezierPathWithRoundedRect:circleRect cornerRadius:radius]; + [path appendPath:circlePath]; + [path setUsesEvenOddFillRule:YES]; + + layer.path = path.CGPath; + layer.fillRule = kCAFillRuleEvenOdd; + layer.fillColor = [UIColor grayColor].CGColor; + layer.opacity = 0.5f; + }]; + [self.view addSubview:maskingView]; + [maskingView autoPinWidthToSuperview]; + [maskingView autoPinHeightToSuperview]; } - (void)viewWillAppear:(BOOL)animated @@ -63,28 +86,12 @@ } } -- (void)viewDidLayoutSubviews -{ - [super viewDidLayoutSubviews]; - [self layoutMaskingView]; -} - - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self stopCapture]; } -- (void)layoutMaskingView -{ - self.maskingView.frame = self.view.frame; - if (self.maskingLayer) { - [self.maskingLayer removeFromSuperlayer]; - } - self.maskingLayer = [self buildCircularMaskingLayer]; - [self.maskingView.layer addSublayer:self.maskingLayer]; -} - - (void)startCapture { self.captureEnabled = YES; @@ -93,8 +100,9 @@ self.capture = [[ZXCapture alloc] init]; self.capture.camera = self.capture.back; self.capture.focusMode = AVCaptureFocusModeContinuousAutoFocus; - self.capture.layer.frame = self.view.frame; + self.capture.layer.frame = self.view.bounds; self.capture.delegate = self; + dispatch_async(dispatch_get_main_queue(), ^{ [self.view.layer addSublayer:self.capture.layer]; [self.view bringSubviewToFront:self.maskingView]; @@ -114,39 +122,13 @@ }); } -- (CAShapeLayer *)buildCircularMaskingLayer -{ - // Add a circular mask - UIBezierPath *path = [UIBezierPath - bezierPathWithRoundedRect:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) - cornerRadius:0]; - CGFloat verticalMargin = 8.0; - CGFloat radius = self.view.frame.size.height / 2.0f - verticalMargin; - - // Center the circle's bounding rectangle - CGFloat horizontalMargin = (self.view.frame.size.width - 2.0f * radius) / 2.0f; - UIBezierPath *circlePath = [UIBezierPath - bezierPathWithRoundedRect:CGRectMake(horizontalMargin, verticalMargin, 2.0f * radius, 2.0f * radius) - cornerRadius:radius]; - [path appendPath:circlePath]; - [path setUsesEvenOddFillRule:YES]; - - CAShapeLayer *fillLayer = [CAShapeLayer layer]; - fillLayer.path = path.CGPath; - fillLayer.fillRule = kCAFillRuleEvenOdd; - fillLayer.fillColor = [UIColor grayColor].CGColor; - fillLayer.opacity = 0.5; - return fillLayer; -} - - (void)captureResult:(ZXCapture *)capture result:(ZXResult *)result { - if (!self.captureEnabled) + if (!self.captureEnabled) { return; + } [self stopCapture]; - // TODO bounding rectangle - // Vibrate AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); diff --git a/Signal/src/util/UIViewController+CameraPermissions.h b/Signal/src/util/UIViewController+CameraPermissions.h index 12f8821c2..8cfede0e5 100644 --- a/Signal/src/util/UIViewController+CameraPermissions.h +++ b/Signal/src/util/UIViewController+CameraPermissions.h @@ -1,17 +1,15 @@ // -// UIViewController+CameraPermissions.h -// Signal -// -// Created by Jarosław Pawlak on 18.10.2016. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface UIViewController (CameraPermissions) +- (void)ows_askForCameraPermissions:(void (^)())permissionsGrantedCallback; + - (void)ows_askForCameraPermissions:(void (^)())permissionsGrantedCallback - alertActionHandler:(nullable void (^)())alertActionHandler; + failureCallback:(nullable void (^)())failureCallback; @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/UIViewController+CameraPermissions.m b/Signal/src/util/UIViewController+CameraPermissions.m index d5a492f6c..59fba0f6c 100644 --- a/Signal/src/util/UIViewController+CameraPermissions.m +++ b/Signal/src/util/UIViewController+CameraPermissions.m @@ -12,12 +12,24 @@ NS_ASSUME_NONNULL_BEGIN @implementation UIViewController (CameraPermissions) - (void)ows_askForCameraPermissions:(void (^)())permissionsGrantedCallback - alertActionHandler:(nullable void (^)())alertActionHandler { + [self ows_askForCameraPermissions:permissionsGrantedCallback failureCallback:nil]; +} + +- (void)ows_askForCameraPermissions:(void (^)())permissionsGrantedCallback + failureCallback:(nullable void (^)())failureCallback +{ + if (!failureCallback) { + failureCallback = ^{ + }; + } + if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { DDLogError(@"Camera ImagePicker source not available"); + failureCallback(); return; } + AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (status == AVAuthorizationStatusDenied) { UIAlertController* alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_TITLE", @"Alert title") @@ -27,30 +39,34 @@ NS_ASSUME_NONNULL_BEGIN NSString *settingsTitle = NSLocalizedString(@"OPEN_SETTINGS_BUTTON", @"Button text which opens the settings app"); UIAlertAction *openSettingsAction = [UIAlertAction actionWithTitle:settingsTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [[UIApplication sharedApplication] openSystemSettings]; - if (alertActionHandler) { - alertActionHandler(); - } + failureCallback(); }]; [alert addAction:openSettingsAction]; UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"DISMISS_BUTTON_TEXT", nil) style:UIAlertActionStyleCancel - handler:alertActionHandler]; + handler:^(UIAlertAction *action) { + failureCallback(); + }]; [alert addAction:dismissAction]; [self presentViewController:alert animated:YES completion:nil]; } else if (status == AVAuthorizationStatusAuthorized) { permissionsGrantedCallback(); } else if (status == AVAuthorizationStatusNotDetermined) { - [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { - if (granted) { - dispatch_async(dispatch_get_main_queue(), ^{ - permissionsGrantedCallback(); - }); - } - }]; + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo + completionHandler:^(BOOL granted) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (granted) { + permissionsGrantedCallback(); + } else { + failureCallback(); + } + }); + }]; } else { DDLogError(@"Unknown AVAuthorizationStatus: %ld", (long)status); + failureCallback(); } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 0f02212e4..485199c05 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1090,6 +1090,9 @@ /* label presented once scanning (camera) view is visible. */ "SCAN_CODE_INSTRUCTIONS" = "Scan the QR Code on your contact's device."; +/* Title for the 'scan QR code' view. */ +"SCAN_QR_CODE_VIEW_TITLE" = "Scan QR Code"; + /* No comment provided by engineer. */ "SEARCH_BYNAMEORNUMBER_PLACEHOLDER_TEXT" = "Search by name or number"; From 5a867e3f6cf82d524c44c08df92235ae5baf1d3b Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 9 Jun 2017 07:27:36 -0400 Subject: [PATCH 2/4] Respond to CR. // FREEBIE --- .../FingerprintViewController.m | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Signal/src/ViewControllers/FingerprintViewController.m b/Signal/src/ViewControllers/FingerprintViewController.m index 519d5da65..a5df8ec38 100644 --- a/Signal/src/ViewControllers/FingerprintViewController.m +++ b/Signal/src/ViewControllers/FingerprintViewController.m @@ -35,8 +35,34 @@ typedef void (^CustomLayoutBlock)(); @implementation CustomLayoutView +- (instancetype)init +{ + if (self = [super init]) { + self.translatesAutoresizingMaskIntoConstraints = NO; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + self.translatesAutoresizingMaskIntoConstraints = NO; + } + return self; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super initWithCoder:aDecoder]) { + self.translatesAutoresizingMaskIntoConstraints = NO; + } + return self; +} + - (void)layoutSubviews { + [super layoutSubviews]; + self.layoutBlock(); } @@ -191,6 +217,7 @@ typedef void (^CustomLayoutBlock)(); UIImageView *fingerprintImageView = [UIImageView new]; fingerprintImageView.image = self.fingerprint.image; + // Don't antialias QR Codes. fingerprintImageView.layer.magnificationFilter = kCAFilterNearest; fingerprintImageView.layer.minificationFilter = kCAFilterNearest; [fingerprintView addSubview:fingerprintImageView]; From 5264602106b233c49b249d453ce01b054eba7cc8 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 9 Jun 2017 07:32:16 -0400 Subject: [PATCH 3/4] Move QR code scanning to a separate view. // FREEBIE --- Signal/src/util/UIViewController+CameraPermissions.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Signal/src/util/UIViewController+CameraPermissions.m b/Signal/src/util/UIViewController+CameraPermissions.m index 59fba0f6c..58abd9685 100644 --- a/Signal/src/util/UIViewController+CameraPermissions.m +++ b/Signal/src/util/UIViewController+CameraPermissions.m @@ -19,6 +19,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)ows_askForCameraPermissions:(void (^)())permissionsGrantedCallback failureCallback:(nullable void (^)())failureCallback { + // Avoid nil tests below. if (!failureCallback) { failureCallback = ^{ }; From 051b005582a623679196bdda29a273c4345f534b Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 9 Jun 2017 07:48:12 -0400 Subject: [PATCH 4/4] Move QR code scanning to a separate view. // FREEBIE --- .../ConversationView/MessagesViewController.m | 2 +- .../src/ViewControllers/FingerprintViewScanController.m | 9 +++++---- .../ViewControllers/OWSQRCodeScanningViewController.m | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m index 012aacb8f..1b481258c 100644 --- a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m +++ b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m @@ -2535,7 +2535,7 @@ typedef enum : NSUInteger { - (void)createScrollDownButton { - const CGFloat kScrollDownButtonSize = round(ScaleFromIPhone5To7Plus(35.f, 40.f)); + const CGFloat kScrollDownButtonSize = ScaleFromIPhone5To7Plus(35.f, 40.f); UIButton *scrollDownButton = [UIButton buttonWithType:UIButtonTypeCustom]; self.scrollDownButton = scrollDownButton; scrollDownButton.backgroundColor = [UIColor colorWithWhite:0.95f alpha:1.f]; diff --git a/Signal/src/ViewControllers/FingerprintViewScanController.m b/Signal/src/ViewControllers/FingerprintViewScanController.m index be702fd5b..1de09d949 100644 --- a/Signal/src/ViewControllers/FingerprintViewScanController.m +++ b/Signal/src/ViewControllers/FingerprintViewScanController.m @@ -79,15 +79,16 @@ NS_ASSUME_NONNULL_BEGIN 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.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(14.f, 18.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]; + [cameraInstructionLabel autoPinWidthToSuperviewWithMargin:ScaleFromIPhone5To7Plus(16.f, 30.f)]; + CGFloat instructionsVMargin = ScaleFromIPhone5To7Plus(10.f, 20.f); + [cameraInstructionLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:instructionsVMargin]; + [cameraInstructionLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:instructionsVMargin]; } - (void)viewWillAppear:(BOOL)animated diff --git a/Signal/src/ViewControllers/OWSQRCodeScanningViewController.m b/Signal/src/ViewControllers/OWSQRCodeScanningViewController.m index d354426bf..25bc74cfa 100644 --- a/Signal/src/ViewControllers/OWSQRCodeScanningViewController.m +++ b/Signal/src/ViewControllers/OWSQRCodeScanningViewController.m @@ -57,8 +57,8 @@ [maskingView setConfigureShapeLayerBlock:^(CAShapeLayer *layer, CGRect bounds) { // Add a circular mask UIBezierPath *path = [UIBezierPath bezierPathWithRect:bounds]; - CGFloat verticalMargin = 8.0; - CGFloat radius = MIN(bounds.size.width, bounds.size.height) * 0.5f - verticalMargin; + CGFloat margin = ScaleFromIPhone5To7Plus(8.f, 16.f); + CGFloat radius = MIN(bounds.size.width, bounds.size.height) * 0.5f - margin; // Center the circle's bounding rectangle CGRect circleRect = CGRectMake(