Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verification by DM: Support QR code #3030

Merged
merged 24 commits into from
Mar 17, 2020
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9f88c2d
Create CameraAccessManager to handle camera availability and authoriz…
SBiOSoftWhare Mar 12, 2020
dce1c78
Create CameraAccessAlertPresenter to present common alerts related to…
SBiOSoftWhare Mar 12, 2020
4ab25db
CameraPresenter: Use CameraAccessManager and CameraAccessAlertPresenter.
SBiOSoftWhare Mar 12, 2020
1def653
Create a common close button.
SBiOSoftWhare Mar 12, 2020
5a740db
Create QRCodeGenerator to generate QR image from data.
SBiOSoftWhare Mar 12, 2020
40e393a
Create QRCodeReaderViewController used to scan a QR code.
SBiOSoftWhare Mar 12, 2020
c3c047c
Add camera button assets.
SBiOSoftWhare Mar 13, 2020
d9d7537
QR code verification: Add localizations.
SBiOSoftWhare Mar 13, 2020
cb2c7e8
QR code verification: Add scanning verification screen.
SBiOSoftWhare Mar 13, 2020
45a0f08
QR code verification: Implement scanning verification view model and …
SBiOSoftWhare Mar 13, 2020
409c078
QR code verification: Add KeyVerificationService specific to Riot.
SBiOSoftWhare Mar 13, 2020
e8fed6a
Key verification: Update data loading flow for new QR code screen.
SBiOSoftWhare Mar 13, 2020
da19aff
User verification: Update start verification flow.
SBiOSoftWhare Mar 13, 2020
e70e2f9
QR code verification: Update scanning verification screen.
SBiOSoftWhare Mar 13, 2020
b502b60
User verification: Check cross-signing bootstrap before start.
SBiOSoftWhare Mar 13, 2020
a46ff04
Device verification: Handle QR code verification.
SBiOSoftWhare Mar 13, 2020
c86f9e7
AppDelegate: Fix incoming key verification request alert crash.
SBiOSoftWhare Mar 13, 2020
3ffffbb
Key verification: Move SAS verification flow files.
SBiOSoftWhare Mar 13, 2020
c3fc03d
Add ZXing pod.
SBiOSoftWhare Mar 13, 2020
4bf3fc5
Update changes
SBiOSoftWhare Mar 13, 2020
3292ed1
KeyVerificationVerifyByScanningViewModel: Remove useless commented code.
SBiOSoftWhare Mar 17, 2020
3548dc4
Move QR code reader and generator classes in their own module.
SBiOSoftWhare Mar 17, 2020
209602f
Move QR code reader and generator classes in their own module.
SBiOSoftWhare Mar 17, 2020
4402331
KeyVerificationVerifyByScanningViewModel: Remove pending QR code tran…
SBiOSoftWhare Mar 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Improvements:
* Room decoration: Remove horizontal empty space when there is no decoration badge to set on room message (#2978).
* RoomVC: For a room preview use room canonical alias if present when joining a room.
* Update Matomo app id (#3001)
* Verification by DM: Support QR code (#2921).

Bug fix:
* Fix error when joining some public rooms, thanks to @chrismoos (PR #2888).
Expand Down
3 changes: 2 additions & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ abstract_target 'RiotPods' do

target "Riot" do
import_MatrixKit
pod 'DGCollectionViewLeftAlignFlowLayout', '~> 1.0.4'
pod 'DGCollectionViewLeftAlignFlowLayout', '~> 1.0.4'
pod 'ZXingObjC', '~> 3.6.5'

target 'RiotTests' do
inherit! :search_paths
Expand Down
8 changes: 7 additions & 1 deletion Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ PODS:
- SwiftLint (0.36.0)
- SwiftUTI (1.0.7)
- zxcvbn-ios (1.0.4)
- ZXingObjC (3.6.5):
- ZXingObjC/All (= 3.6.5)
- ZXingObjC/All (3.6.5)

DEPENDENCIES:
- cmark
Expand All @@ -118,6 +121,7 @@ DEPENDENCIES:
- SwiftLint (~> 0.36.0)
- SwiftUTI (from `https://github.com/speramusinc/SwiftUTI.git`, branch `master`)
- zxcvbn-ios
- ZXingObjC (~> 3.6.5)

SPEC REPOS:
trunk:
Expand All @@ -141,6 +145,7 @@ SPEC REPOS:
- SwiftGen
- SwiftLint
- zxcvbn-ios
- ZXingObjC

EXTERNAL SOURCES:
SwiftUTI:
Expand Down Expand Up @@ -174,7 +179,8 @@ SPEC CHECKSUMS:
SwiftLint: fc9859e4e1752340664851f667bb1898b9c90114
SwiftUTI: 917993c124f8eac25e88ced0202fc58d7eb50fa8
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb

PODFILE CHECKSUM: 881048fb17d68dd834b18e23929482600daca7f3
PODFILE CHECKSUM: 185f69d794948490227a5ab4ce9ce7945e3ed486

COCOAPODS: 1.8.4
136 changes: 122 additions & 14 deletions Riot.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

52 changes: 27 additions & 25 deletions Riot/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -764,35 +764,37 @@ - (void)keyVerificationRequestDidChangeNotification:(NSNotification *)notificati

NSString *alertMessage = [NSString stringWithFormat:NSLocalizedStringFromTable(@"key_verification_incoming_request_incoming_alert_message", @"Vector", nil), senderInfo];

self.incomingKeyVerificationRequestAlertController = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_title", @"Vector", nil)
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_title", @"Vector", nil)
message:alertMessage
preferredStyle:UIAlertControllerStyleAlert];

[self.incomingKeyVerificationRequestAlertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_approval_accept", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[self presentIncomingKeyVerificationRequest:keyVerificationByDMRequest inSession:self.mxSessions.firstObject];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_approval_accept", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[self presentIncomingKeyVerificationRequest:keyVerificationByDMRequest inSession:self.mxSessions.firstObject];
}]];

[self.incomingKeyVerificationRequestAlertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_approval_decline", @"Vector", nil)
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action)
{
[keyVerificationByDMRequest cancelWithCancelCode:MXTransactionCancelCode.user success:^{
} failure:^(NSError * _Nonnull error) {
NSLog(@"[AppDelegate][KeyVerification] Fail to cancel incoming key verification request with error: %@", error);
}];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"key_verification_tile_request_incoming_approval_decline", @"Vector", nil)
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action)
{
[keyVerificationByDMRequest cancelWithCancelCode:MXTransactionCancelCode.user success:^{

} failure:^(NSError * _Nonnull error) {
NSLog(@"[AppDelegate][KeyVerification] Fail to cancel incoming key verification request with error: %@", error);
}];
}]];

[self.incomingKeyVerificationRequestAlertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"later", @"Vector", nil)
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action)
{
}]];
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"later", @"Vector", nil)
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action)
{
}]];

[self showNotificationAlert:self.incomingKeyVerificationRequestAlertController];
[self showNotificationAlert:alertController];

self.incomingKeyVerificationRequestAlertController = alertController;
}];
}
}
Expand Down
23 changes: 23 additions & 0 deletions Riot/Assets/Images.xcassets/Common/camera.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "camera.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "camera@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "camera@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,9 @@

// MARK: - Key Verification

"key_verification_bootstrap_not_setup_title" = "Error";
"key_verification_bootstrap_not_setup_message" = "You have to bootstrap cross-signing";

// Tiles

"key_verification_tile_request_incoming_title" = "Verification request";
Expand All @@ -1162,6 +1165,18 @@

"key_verification_incoming_request_incoming_alert_message" = "%@ wants to verify";

// MARK: QR code

"key_verification_verify_qr_code_title" = "Verify by scanning";
"key_verification_verify_qr_code_information" = "Scan the code to securely verify each other.";
"key_verification_verify_qr_code_scan_code_action" = "Scan their code";
"key_verification_verify_qr_code_cannot_scan_action" = "Can't scan?";

"key_verification_verify_qr_code_other_scan_my_code_title" = "Did the other user successfully scan the QR code?";

"key_verification_verify_qr_code_scan_other_code_success_title" = "Code validated!";
"key_verification_verify_qr_code_scan_other_code_success_message" = "QR code has been successfully validated.";

// MARK: - User verification

// Start
Expand Down
1 change: 1 addition & 0 deletions Riot/Generated/Images.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ internal enum Asset {
internal static let riotIconCallkit = ImageAsset(name: "riot_icon_callkit")
internal static let adminIcon = ImageAsset(name: "admin_icon")
internal static let backIcon = ImageAsset(name: "back_icon")
internal static let camera = ImageAsset(name: "camera")
internal static let chevron = ImageAsset(name: "chevron")
internal static let closeButton = ImageAsset(name: "close_button")
internal static let disclosureIcon = ImageAsset(name: "disclosure_icon")
Expand Down
10 changes: 10 additions & 0 deletions Riot/Generated/Storyboards.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ internal enum StoryboardScene {

internal static let initialScene = InitialSceneType<Riot.KeyBackupSetupSuccessFromRecoveryKeyViewController>(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self)
}
internal enum KeyVerificationVerifyByScanningViewController: StoryboardType {
internal static let storyboardName = "KeyVerificationVerifyByScanningViewController"

internal static let initialScene = InitialSceneType<Riot.KeyVerificationVerifyByScanningViewController>(storyboard: KeyVerificationVerifyByScanningViewController.self)
}
internal enum QRCodeReaderViewController: StoryboardType {
internal static let storyboardName = "QRCodeReaderViewController"

internal static let initialScene = InitialSceneType<Riot.QRCodeReaderViewController>(storyboard: QRCodeReaderViewController.self)
}
internal enum ReactionHistoryViewController: StoryboardType {
internal static let storyboardName = "ReactionHistoryViewController"

Expand Down
36 changes: 36 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1470,6 +1470,14 @@ internal enum VectorL10n {
internal static var keyBackupSetupTitle: String {
return VectorL10n.tr("Vector", "key_backup_setup_title")
}
/// You have to bootstrap cross-signing
internal static var keyVerificationBootstrapNotSetupMessage: String {
return VectorL10n.tr("Vector", "key_verification_bootstrap_not_setup_message")
}
/// Error
internal static var keyVerificationBootstrapNotSetupTitle: String {
return VectorL10n.tr("Vector", "key_verification_bootstrap_not_setup_title")
}
/// %@ wants to verify
internal static func keyVerificationIncomingRequestIncomingAlertMessage(_ p1: String) -> String {
return VectorL10n.tr("Vector", "key_verification_incoming_request_incoming_alert_message", p1)
Expand Down Expand Up @@ -1534,6 +1542,34 @@ internal enum VectorL10n {
internal static var keyVerificationVerifiedUserDescription2: String {
return VectorL10n.tr("Vector", "key_verification_verified_user_description_2")
}
/// Can't scan?
internal static var keyVerificationVerifyQrCodeCannotScanAction: String {
return VectorL10n.tr("Vector", "key_verification_verify_qr_code_cannot_scan_action")
}
/// Scan the code to securely verify each other.
internal static var keyVerificationVerifyQrCodeInformation: String {
return VectorL10n.tr("Vector", "key_verification_verify_qr_code_information")
}
/// Did the other user successfully scan the QR code?
internal static var keyVerificationVerifyQrCodeOtherScanMyCodeTitle: String {
return VectorL10n.tr("Vector", "key_verification_verify_qr_code_other_scan_my_code_title")
}
/// Scan their code
internal static var keyVerificationVerifyQrCodeScanCodeAction: String {
return VectorL10n.tr("Vector", "key_verification_verify_qr_code_scan_code_action")
}
/// QR code has been successfully validated.
internal static var keyVerificationVerifyQrCodeScanOtherCodeSuccessMessage: String {
return VectorL10n.tr("Vector", "key_verification_verify_qr_code_scan_other_code_success_message")
}
/// Code validated!
internal static var keyVerificationVerifyQrCodeScanOtherCodeSuccessTitle: String {
return VectorL10n.tr("Vector", "key_verification_verify_qr_code_scan_other_code_success_title")
}
/// Verify by scanning
internal static var keyVerificationVerifyQrCodeTitle: String {
return VectorL10n.tr("Vector", "key_verification_verify_qr_code_title")
}
/// Verify this user by confirming the following unique emoji appears on their screen, in the same order.
internal static var keyVerificationVerifyUserTitleEmoji: String {
return VectorL10n.tr("Vector", "key_verification_verify_user_title_emoji")
Expand Down
61 changes: 61 additions & 0 deletions Riot/Modules/Camera/CameraAccessAlertPresenter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2020 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Foundation

final class CameraAccessAlertPresenter {

// MARK: - Public

func presentPermissionDeniedAlert(from presentingViewController: UIViewController, animated: Bool) {
guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else {
return
}

let appDisplayName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? ""

let alert = UIAlertController(title: VectorL10n.camera, message: VectorL10n.cameraAccessNotGranted(appDisplayName), preferredStyle: .alert)

let cancelActionTitle = Bundle.mxk_localizedString(forKey: "ok")
let cancelAction = UIAlertAction(title: cancelActionTitle, style: .cancel, handler: { _ in
})

let settingsActionTitle = Bundle.mxk_localizedString(forKey: "settings")
let settingsAction = UIAlertAction(title: settingsActionTitle, style: .default, handler: { _ in
UIApplication.shared.open(settingsURL, options: [:], completionHandler: { (succeed) in
if !succeed {
print("[CameraPresenter] Fails to open settings")
}
})
})

alert.addAction(cancelAction)
alert.addAction(settingsAction)

presentingViewController.present(alert, animated: animated, completion: nil)
}

func presentCameraUnavailableAlert(from presentingViewController: UIViewController, animated: Bool) {

let alert = UIAlertController(title: VectorL10n.camera, message: VectorL10n.cameraUnavailable, preferredStyle: .alert)

let okAction = UIAlertAction(title: VectorL10n.accept, style: .default, handler: nil)

alert.addAction(okAction)

presentingViewController.present(alert, animated: true, completion: nil)
}
}
61 changes: 61 additions & 0 deletions Riot/Modules/Camera/CameraAccessManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright 2020 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Foundation

/// CameraAccessManager handles camera availability and authorization.
final class CameraAccessManager {

// MARK: - Properties

var isCameraAvailable: Bool {
return UIImagePickerController.isSourceTypeAvailable(.camera)
}

var isCameraAccessGranted: Bool {
return AVCaptureDevice.authorizationStatus(for: .video) == .authorized
}

// MARK: - Public

func askAndRequestCameraAccessIfNeeded(completion: @escaping (_ granted: Bool) -> Void) {

let authorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)

switch authorizationStatus {
case .authorized:
completion(true)
case .notDetermined:
self.requestCameraAccess(completion: { (granted) in
completion(granted)
})
case .denied, .restricted:
completion(false)
@unknown default:
break
}
}

// MARK: - Private

private func requestCameraAccess(completion: @escaping (_ granted: Bool) -> Void) {
AVCaptureDevice.requestAccess(for: .video) { granted in
DispatchQueue.main.async {
completion(granted)
}
}
}
}
Loading