diff --git a/Vector/Assets/en.lproj/Vector.strings b/Vector/Assets/en.lproj/Vector.strings index e7c5db4a7..d8397b0d5 100644 --- a/Vector/Assets/en.lproj/Vector.strings +++ b/Vector/Assets/en.lproj/Vector.strings @@ -1,5 +1,6 @@ /* Copyright 2015 OpenMarket Ltd + Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -292,6 +293,7 @@ "settings_crypto_device_name" = "Device name: "; "settings_crypto_device_id" = "\nDevice ID: "; "settings_crypto_device_key" = "\nDevice key: "; +"settings_crypto_export" = "Export keys"; // Room Details "room_details_title" = "Room Details"; diff --git a/Vector/ViewController/SettingsViewController.h b/Vector/ViewController/SettingsViewController.h index a5b16a25b..8b80cc41d 100644 --- a/Vector/ViewController/SettingsViewController.h +++ b/Vector/ViewController/SettingsViewController.h @@ -20,7 +20,7 @@ #import "MediaPickerViewController.h" -@interface SettingsViewController : MXKViewController +@interface SettingsViewController : MXKViewController @property (nonatomic) IBOutlet UITableView *tableView; diff --git a/Vector/ViewController/SettingsViewController.m b/Vector/ViewController/SettingsViewController.m index 822677c98..8b707d45d 100644 --- a/Vector/ViewController/SettingsViewController.m +++ b/Vector/ViewController/SettingsViewController.m @@ -1,5 +1,6 @@ /* Copyright 2015 OpenMarket Ltd + Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,6 +28,8 @@ #import #import +#import "MXKEncryptionKeysExportView.h" + #import "OLMKit/OLMKit.h" enum { @@ -70,6 +73,10 @@ enum { #define LABS_CRYPTO_INDEX 0 #define LABS_COUNT 1 +#define CRYPTOGRAPHY_INFO_INDEX 0 +#define CRYPTOGRAPHY_EXPORT_INDEX 1 +#define CRYPTOGRAPHY_COUNT 2 + #define SECTION_TITLE_PADDING_WHEN_HIDDEN 0.01f typedef void (^blockSettingsViewController_onReadyToDestroy)(); @@ -137,6 +144,11 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); // UIAlertController *resetPwdAlertController; + + // The document interaction Controller used to export e2e keys + UIDocumentInteractionController *documentInteractionController; + NSURL *keyExportsFile; + NSTimer *keyExportsFileDeletionTimer; } /** @@ -220,6 +232,13 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); - (void)destroy { + if (documentInteractionController) + { + [documentInteractionController dismissPreviewAnimated:NO]; + [documentInteractionController dismissMenuAnimated:NO]; + documentInteractionController = nil; + } + if (isSavingInProgress || isResetPwdInProgress || isEmailBindingInProgress) { __weak typeof(self) weakSelf = self; @@ -798,7 +817,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); // Check whether this section is visible. if (self.mainSession.crypto) { - count = 1; + count = CRYPTOGRAPHY_COUNT; } } return count; @@ -1323,16 +1342,39 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); } else if (section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX) { - MXKTableViewCell *cryptoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]]; - if (!cryptoCell) + if (row == CRYPTOGRAPHY_INFO_INDEX) { - cryptoCell = [[MXKTableViewCell alloc] init]; + MXKTableViewCell *cryptoCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]]; + if (!cryptoCell) + { + cryptoCell = [[MXKTableViewCell alloc] init]; + } + + cryptoCell.textLabel.attributedText = [self cryptographyInformation]; + cryptoCell.textLabel.numberOfLines = 0; + + cell = cryptoCell; + } + else if (row == CRYPTOGRAPHY_EXPORT_INDEX) + { + MXKTableViewCellWithButton *exportKeysBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]]; + if (!exportKeysBtnCell) + { + exportKeysBtnCell = [[MXKTableViewCellWithButton alloc] init]; + } + + NSString *btnTitle = NSLocalizedStringFromTable(@"settings_crypto_export", @"Vector", nil); + [exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal]; + [exportKeysBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted]; + [exportKeysBtnCell.mxkButton setTintColor:kVectorColorGreen]; + exportKeysBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17]; + + [exportKeysBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; + [exportKeysBtnCell.mxkButton addTarget:self action:@selector(exportEncryptionKeys:) forControlEvents:UIControlEventTouchUpInside]; + exportKeysBtnCell.mxkButton.accessibilityIdentifier = nil; + + cell = exportKeysBtnCell; } - - cryptoCell.textLabel.attributedText = [self cryptographyInformation]; - cryptoCell.textLabel.numberOfLines = 0; - - cell = cryptoCell; } return cell; @@ -1427,7 +1469,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); [label sizeToFit]; return label.frame.size.height + 16; } - else if (indexPath.section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX) + else if (indexPath.section == SETTINGS_SECTION_CRYPTOGRAPHY_INDEX && indexPath.row == CRYPTOGRAPHY_INFO_INDEX) { UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 50)]; label.numberOfLines = 0; @@ -2180,6 +2222,65 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); [self presentViewController:navigationController animated:YES completion:nil]; } +- (void)exportEncryptionKeys:(UITapGestureRecognizer *)recognizer +{ + [currentAlert dismiss:NO]; + + MXKEncryptionKeysExportView *exportView = [[MXKEncryptionKeysExportView alloc] initWithMatrixSession:self.mainSession]; + currentAlert = exportView; + + // Use a temporary file for the export + keyExportsFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"riot-keys.txt"]]; + + // Make sure the file is empty + [self deleteKeyExportFile]; + + // Show the export dialog + __weak typeof(self) weakSelf = self; + [exportView showInViewController:self toExportKeysToFile:keyExportsFile onComplete:^(BOOL success) { + + if (weakSelf && success) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + // Let another app handling this file + self->documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:keyExportsFile]; + [self->documentInteractionController setDelegate:self]; + + if ([self->documentInteractionController presentOptionsMenuFromRect:self.view.frame inView:self.view animated:YES]) + { + // We want to delete the temp keys file after it has been processed by the other app. + // We use [UIDocumentInteractionControllerDelegate didEndSendingToApplication] for that + // but it is not reliable for all cases (see http://stackoverflow.com/a/21867096). + // So, arm a timer to auto delete the file after 10mins. + keyExportsFileDeletionTimer = [NSTimer scheduledTimerWithTimeInterval:600 target:self selector:@selector(deleteKeyExportFile) userInfo:self repeats:NO]; + } + else + { + self->documentInteractionController = nil; + [self deleteKeyExportFile]; + } + } + }]; +} + +- (void)deleteKeyExportFile +{ + // Cancel the deletion timer if it is still here + if (keyExportsFileDeletionTimer) + { + [keyExportsFileDeletionTimer invalidate]; + keyExportsFileDeletionTimer = nil; + } + + // And delete the file + if (keyExportsFile && [[NSFileManager defaultManager] fileExistsAtPath:keyExportsFile.path]) + { + [[NSFileManager defaultManager] removeItemAtPath:keyExportsFile.path error:nil]; + } +} + #pragma mark - MediaPickerViewController Delegate - (void)dismissMediaPicker @@ -2449,4 +2550,17 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(); [self presentViewController:resetPwdAlertController animated:YES completion:nil]; } +#pragma mark - UIDocumentInteractionControllerDelegate + +- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application +{ + // If iOS wants to call this method, this is the right time to remove the file + [self deleteKeyExportFile]; +} + +- (void)documentInteractionControllerDidDismissOptionsMenu:(UIDocumentInteractionController *)controller +{ + documentInteractionController = nil; +} + @end