diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 01e2a699c..29912f5ee 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -561,6 +561,7 @@ B821F2F82272CED3002C88C0 /* AccountDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821F2F72272CED3002C88C0 /* AccountDetailsViewController.swift */; }; B821F2FA2272CEEE002C88C0 /* SeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B821F2F92272CEEE002C88C0 /* SeedViewController.swift */; }; B825848B230F94FE001B41CB /* QRCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B825848A230F94FE001B41CB /* QRCodeViewController.swift */; }; + B8258493230FA5E9001B41CB /* ScanQRCodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B8258492230FA5E9001B41CB /* ScanQRCodeViewController.m */; }; B845B4D4230CD09100D759F0 /* LokiGroupChatPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = B845B4D3230CD09000D759F0 /* LokiGroupChatPoller.swift */; }; B846365B22B7418B00AF1514 /* Identicon+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */; }; B89841E322B7579F00B1BDC6 /* NewConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B89841E222B7579F00B1BDC6 /* NewConversationViewController.swift */; }; @@ -1352,6 +1353,8 @@ B821F2F72272CED3002C88C0 /* AccountDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDetailsViewController.swift; sourceTree = ""; }; B821F2F92272CEEE002C88C0 /* SeedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedViewController.swift; sourceTree = ""; }; B825848A230F94FE001B41CB /* QRCodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeViewController.swift; sourceTree = ""; }; + B8258491230FA5DA001B41CB /* ScanQRCodeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScanQRCodeViewController.h; sourceTree = ""; }; + B8258492230FA5E9001B41CB /* ScanQRCodeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanQRCodeViewController.m; sourceTree = ""; }; B845B4D3230CD09000D759F0 /* LokiGroupChatPoller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiGroupChatPoller.swift; sourceTree = ""; }; B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Identicon+ObjC.swift"; sourceTree = ""; }; B89841E222B7579F00B1BDC6 /* NewConversationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationViewController.swift; sourceTree = ""; }; @@ -2618,6 +2621,8 @@ 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */, B845B4D3230CD09000D759F0 /* LokiGroupChatPoller.swift */, B825848A230F94FE001B41CB /* QRCodeViewController.swift */, + B8258491230FA5DA001B41CB /* ScanQRCodeViewController.h */, + B8258492230FA5E9001B41CB /* ScanQRCodeViewController.m */, ); path = Loki; sourceTree = ""; @@ -3801,6 +3806,7 @@ 34CA631B2097806F00E526A0 /* OWSContactShareView.m in Sources */, 34D1F0861F8678AA0066283D /* ConversationViewController.m in Sources */, 3427C64320F500E000EEC730 /* OWSMessageTimerView.m in Sources */, + B8258493230FA5E9001B41CB /* ScanQRCodeViewController.m in Sources */, B90418E6183E9DD40038554A /* DateUtil.m in Sources */, 3448E15E221333F5004B052E /* OnboardingController.swift in Sources */, 340FC8BD204DAC8D007AEB0F /* ShowGroupMembersViewController.m in Sources */, diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index ea98f085f..752b44856 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -127,7 +127,7 @@ NSAppleMusicUsageDescription Signal needs to use Apple Music to play media attachments. NSCameraUsageDescription - Signal uses your camera to take photos and for video calls. + Loki Messenger needs camera access to scan QR codes. NSContactsUsageDescription Signal uses your contacts to find users you know. We do not store your contacts on the server. NSFaceIDUsageDescription diff --git a/Signal/src/Loki/NewConversationViewController.swift b/Signal/src/Loki/NewConversationViewController.swift index 0c12b230f..8d41efe70 100644 --- a/Signal/src/Loki/NewConversationViewController.swift +++ b/Signal/src/Loki/NewConversationViewController.swift @@ -1,6 +1,6 @@ @objc(LKNewConversationViewController) -final class NewConversationViewController : OWSViewController { +final class NewConversationViewController : OWSViewController, OWSQRScannerDelegate { // MARK: Components private lazy var publicKeyTextField: UITextField = { @@ -34,20 +34,29 @@ final class NewConversationViewController : OWSViewController { explanationLabel.text = NSLocalizedString("Enter the public key of the person you'd like to securely message. They can share their public key with you by going into Loki Messenger's in-app settings and clicking \"Share Public Key\".", comment: "") explanationLabel.numberOfLines = 0 explanationLabel.lineBreakMode = .byWordWrapping - // Button - let buttonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() - let buttonHeight = buttonFont.pointSize * 48 / 17 - let startNewConversationButton = OWSFlatButton.button(title: NSLocalizedString("Next", comment: ""), font: buttonFont, titleColor: .white, backgroundColor: .lokiGreen(), target: self, selector: #selector(startNewConversationIfPossible)) - startNewConversationButton.autoSetDimension(.height, toSize: buttonHeight) + // QR code button + let qrCodeButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() + let qrCodeButtonHeight = qrCodeButtonFont.pointSize * 48 / 17 + let qrCodeButton = OWSFlatButton.button(title: NSLocalizedString("Scan a QR Code Instead", comment: ""), font: qrCodeButtonFont, titleColor: .lokiGreen(), backgroundColor: .clear, target: self, selector: #selector(scanQRCode)) + qrCodeButton.setBackgroundColors(upColor: .clear, downColor: .clear) + qrCodeButton.autoSetDimension(.height, toSize: qrCodeButtonHeight) + qrCodeButton.button.contentHorizontalAlignment = .left + // Next button + let nextButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() + let nextButtonHeight = nextButtonFont.pointSize * 48 / 17 + let nextButton = OWSFlatButton.button(title: NSLocalizedString("Next", comment: ""), font: nextButtonFont, titleColor: .white, backgroundColor: .lokiGreen(), target: self, selector: #selector(startNewConversationIfPossible)) + nextButton.autoSetDimension(.height, toSize: nextButtonHeight) // Stack view let stackView = UIStackView(arrangedSubviews: [ publicKeyTextField, UIView.spacer(withHeight: 8), separator, - UIView.spacer(withHeight: 8), + UIView.spacer(withHeight: 24), explanationLabel, + UIView.spacer(withHeight: 8), + qrCodeButton, UIView.vStretchingSpacer(), - startNewConversationButton + nextButton ]) stackView.axis = .vertical stackView.alignment = .fill @@ -69,8 +78,29 @@ final class NewConversationViewController : OWSViewController { dismiss(animated: true, completion: nil) } - @objc private func startNewConversationIfPossible() { + @objc private func scanQRCode() { + ows_ask(forCameraPermissions: { [weak self] hasCameraAccess in + if hasCameraAccess { + let scanQRCodeVC = ScanQRCodeViewController() + scanQRCodeVC.delegate = self + self?.navigationController!.pushViewController(scanQRCodeVC, animated: true) + } else { + // Do nothing + } + }) + } + + func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) { + let hexEncodedPublicKey = string + startNewConversationIfPossible(with: hexEncodedPublicKey) + } + + private func handleNextButtonTapped() { let hexEncodedPublicKey = publicKeyTextField.text?.trimmingCharacters(in: .whitespaces) ?? "" + startNewConversationIfPossible(with: hexEncodedPublicKey) + } + + @objc private func startNewConversationIfPossible(with hexEncodedPublicKey: String) { if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) { let alert = UIAlertController(title: NSLocalizedString("Invalid Public Key", comment: ""), message: NSLocalizedString("Please check the public key you entered and try again.", comment: ""), preferredStyle: .alert) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) diff --git a/Signal/src/Loki/ScanQRCodeViewController.h b/Signal/src/Loki/ScanQRCodeViewController.h new file mode 100644 index 000000000..e0af91258 --- /dev/null +++ b/Signal/src/Loki/ScanQRCodeViewController.h @@ -0,0 +1,12 @@ +#import +#import "OWSQRCodeScanningViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ScanQRCodeViewController : OWSViewController + +@property (nonatomic, weak) UIViewController *delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/Loki/ScanQRCodeViewController.m b/Signal/src/Loki/ScanQRCodeViewController.m new file mode 100644 index 000000000..744d1f6d5 --- /dev/null +++ b/Signal/src/Loki/ScanQRCodeViewController.m @@ -0,0 +1,62 @@ +#import "ScanQRCodeViewController.h" +#import "Session-Swift.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ScanQRCodeViewController () + +@property (nonatomic) OWSQRCodeScanningViewController *qrCodeScanningVC; + +@end + +@implementation ScanQRCodeViewController + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; } + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Background color + self.view.backgroundColor = Theme.backgroundColor; + // QR code scanning VC + self.qrCodeScanningVC = [OWSQRCodeScanningViewController new]; + self.qrCodeScanningVC.scanDelegate = self.delegate; + [self.view addSubview:self.qrCodeScanningVC.view]; + [self.qrCodeScanningVC.view autoPinEdgeToSuperviewEdge:ALEdgeLeading]; + [self.qrCodeScanningVC.view autoPinEdgeToSuperviewEdge:ALEdgeTrailing]; + [self.qrCodeScanningVC.view autoPinToTopLayoutGuideOfViewController:self withInset:0.0]; + [self.qrCodeScanningVC.view autoPinToSquareAspectRatio]; + // Explanation label + UILabel *explanationLabel = [UILabel new]; + explanationLabel.text = NSLocalizedString(@"Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\".", @""); + explanationLabel.textColor = Theme.primaryColor; + explanationLabel.font = UIFont.ows_dynamicTypeSubheadlineClampedFont; + explanationLabel.numberOfLines = 0; + explanationLabel.lineBreakMode = NSLineBreakByWordWrapping; + explanationLabel.textAlignment = NSTextAlignmentCenter; + // Bottom view + UIView *bottomView = [UIView new]; + [self.view addSubview:bottomView]; + [bottomView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:self.qrCodeScanningVC.view]; + [bottomView autoPinEdgeToSuperviewEdge:ALEdgeLeading]; + [bottomView autoPinEdgeToSuperviewEdge:ALEdgeTrailing]; + [bottomView autoPinEdgeToSuperviewEdge:ALEdgeBottom]; + [bottomView addSubview:explanationLabel]; + [explanationLabel autoPinWidthToSuperviewWithMargin:32]; + [explanationLabel autoPinHeightToSuperviewWithMargin:32]; + // Title + self.title = NSLocalizedString(@"Scan QR Code", ""); +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + [UIDevice.currentDevice ows_setOrientation:UIInterfaceOrientationPortrait]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self.qrCodeScanningVC startCapture]; + }); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index 38fccf4f7..ade9e9c9d 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -43,6 +43,7 @@ #import "PrivacySettingsTableViewController.h" #import "ProfileViewController.h" #import "RemoteVideoView.h" +#import "ScanQRCodeViewController.h" #import "SignalApp.h" #import "UIViewController+Permissions.h" #import "ViewControllerUtils.h" diff --git a/Signal/src/util/UIViewController+Permissions.m b/Signal/src/util/UIViewController+Permissions.m index ec9fe4884..7a34e91e4 100644 --- a/Signal/src/util/UIViewController+Permissions.m +++ b/Signal/src/util/UIViewController+Permissions.m @@ -39,8 +39,8 @@ NS_ASSUME_NONNULL_BEGIN AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (status == AVAuthorizationStatusDenied) { UIAlertController *alert = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_TITLE", @"Alert title") - message:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_MESSAGE", @"Alert body") + alertControllerWithTitle:NSLocalizedString(@"Loki Messenger needs camera access to scan QR codes.", @"") + message:NSLocalizedString(@"You can enable camera access in your device settings.", @"") preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *openSettingsAction = diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 043b9612c..0e17a7b9e 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2608,3 +2608,9 @@ "This version of Loki Messenger is no longer supported. Please press OK to reset your account and migrate to the latest version." = "This version of Loki Messenger is no longer supported. Please press OK to reset your account and migrate to the latest version."; "Loki Public Chat" = "Loki Public Chat"; "Show QR Code" = "Show QR Code"; +"This is your personal QR code. Other people can scan it to start a secure conversation with you." = "This is your personal QR code. Other people can scan it to start a secure conversation with you."; +"Scan a QR Code Instead" = "Scan a QR Code Instead"; +"Loki Messenger needs camera access to scan QR codes." = "Loki Messenger needs camera access to scan QR codes."; +"You can enable camera access in your device settings." = "You can enable camera access in your device settings."; +"Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\"." = "Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\"."; +"Scan QR Code" = "Scan QR Code"; diff --git a/SignalMessaging/Views/OWSFlatButton.swift b/SignalMessaging/Views/OWSFlatButton.swift index c11a3aa91..9214f27a0 100644 --- a/SignalMessaging/Views/OWSFlatButton.swift +++ b/SignalMessaging/Views/OWSFlatButton.swift @@ -8,7 +8,7 @@ import SignalServiceKit @objc public class OWSFlatButton: UIView { - private let button: UIButton + public let button: UIButton private var pressedBlock : (() -> Void)?