BackupIdentityViewController.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2012-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "BackupIdentityViewController.h"
  21. #import "QRCodeGenerator.h"
  22. #import "AppDelegate.h"
  23. #import "MyIdentityStore.h"
  24. #import "IdentityBackupStore.h"
  25. #import "UIImage+ColoredImage.h"
  26. #import "AppGroup.h"
  27. #import "Utils.h"
  28. #import "LicenseStore.h"
  29. #import "MDMSetup.h"
  30. #import "ActivityUtil.h"
  31. #import "Threema-Swift.h"
  32. @interface BackupIdentityViewController ()
  33. @end
  34. @implementation BackupIdentityViewController {
  35. UIView *coverView;
  36. UIImageView *zoomingQrImageView;
  37. CGRect qrRect;
  38. CGFloat prevBrightness;
  39. UIImage *qrCodeImage;
  40. MDMSetup *mdmSetup;
  41. }
  42. - (void)viewDidLoad
  43. {
  44. [super viewDidLoad];
  45. self.navigationItem.hidesBackButton = YES;
  46. mdmSetup = [[MDMSetup alloc] initWithSetup:NO];
  47. if (mdmSetup.disableSystemBackups) {
  48. self.phoneBackupSwitch.on = NO;
  49. self.phoneBackupSwitch.enabled = NO;
  50. }
  51. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
  52. }
  53. - (void)viewWillAppear:(BOOL)animated {
  54. [super viewWillAppear:animated];
  55. [self hideFullScreenQrViewAnimated:NO];
  56. /* Wrap backup data manually for display as iOS text wrap engine doesn't always
  57. wrap it nicely. Set special attribute on CopyLabel to ensure that copied text
  58. does not contain line breaks. */
  59. self.identityBackupLabel.textForCopying = self.backupData;
  60. self.identityBackupLabel.text = [self wrapText:self.backupData withFixedWidth:20];
  61. qrCodeImage = [QRCodeGenerator renderQrCodeString:self.backupData withDimension:self.qrImageView.frame.size.width*2];
  62. switch ([Colors getTheme]) {
  63. case ColorThemeDark:
  64. case ColorThemeDarkWork:
  65. self.qrImageView.image = [qrCodeImage imageWithTint:[UIColor whiteColor]];
  66. break;
  67. case ColorThemeLight:
  68. case ColorThemeLightWork:
  69. case ColorThemeUndefined:
  70. self.qrImageView.image = qrCodeImage;
  71. break;
  72. }
  73. }
  74. - (void)viewWillDisappear:(BOOL)animated {
  75. [self hideFullScreenQrViewAnimated:NO];
  76. [super viewWillDisappear:animated];
  77. }
  78. - (void)viewWillLayoutSubviews {
  79. [super viewWillLayoutSubviews];
  80. UIFontDescriptor *fontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleTitle3];
  81. self.identityBackupLabel.font = [UIFont fontWithName:@"Courier" size:fontDescriptor.pointSize];
  82. }
  83. - (void)dealloc {
  84. [[NSNotificationCenter defaultCenter] removeObserver:self];
  85. }
  86. - (void)appResignActive:(NSNotification*)notification {
  87. [self hideFullScreenQrViewAnimated:NO];
  88. }
  89. - (BOOL)shouldAutorotate {
  90. return YES;
  91. }
  92. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  93. if (SYSTEM_IS_IPAD) {
  94. return UIInterfaceOrientationMaskAll;
  95. }
  96. return UIInterfaceOrientationMaskAllButUpsideDown;
  97. }
  98. - (IBAction)doneAction:(id)sender {
  99. if (self.phoneBackupSwitch.on)
  100. [IdentityBackupStore saveIdentityBackup:self.backupData];
  101. else
  102. [IdentityBackupStore deleteIdentityBackup];
  103. [self.navigationController dismissViewControllerAnimated:YES completion:nil];
  104. }
  105. - (IBAction)actionAction:(id)sender {
  106. UIActivityViewController *activityViewController = [ActivityUtil activityViewControllerWithActivityItems:@[self] applicationActivities:@[]];
  107. if (SYSTEM_IS_IPAD == YES) {
  108. CGRect rect = [self.view convertRect:self.view.frame fromView:self.view.superview];
  109. activityViewController.popoverPresentationController.sourceRect = rect;
  110. activityViewController.popoverPresentationController.sourceView = self.view;
  111. }
  112. NSUserDefaults *defaults = [AppGroup userDefaults];
  113. [defaults setDouble:[Utils systemUptime] forKey:@"UIActivityViewControllerOpenTime"];
  114. [defaults synchronize];
  115. [activityViewController setCompletionWithItemsHandler:^(UIActivityType _Nullable activityType, BOOL completed, NSArray * _Nullable returnedItems, NSError * _Nullable activityError) {
  116. NSUserDefaults *defaults = [AppGroup userDefaults];
  117. [defaults removeObjectForKey:@"UIActivityViewControllerOpenTime"];
  118. }];
  119. [self presentViewController:activityViewController animated:YES completion:nil];
  120. }
  121. - (IBAction)phoneBackupSwitchChanged:(id)sender {
  122. [self.tableView reloadData];
  123. }
  124. - (void)fullScreenQrCodeTapped:(UITapGestureRecognizer *)sender {
  125. if (sender.state == UIGestureRecognizerStateEnded) {
  126. [self hideFullScreenQrViewAnimated:YES];
  127. }
  128. }
  129. - (void)showFullScreenQrView {
  130. /* QR code */
  131. qrRect = [self.qrImageView.superview convertRect:self.qrImageView.frame toView:self.view.superview];
  132. /* Center in view with margin */
  133. CGRect contentRect = self.view.superview.frame;
  134. int maxw = contentRect.size.width - 40;
  135. int maxh = contentRect.size.height - 40;
  136. int dim = MIN(maxw, maxh);
  137. CGRect qrTargetRect = CGRectMake((contentRect.size.width - dim) / 2, (contentRect.size.height - dim) / 2, dim, dim);
  138. coverView = [[UIView alloc] initWithFrame:self.view.frame];
  139. coverView.alpha = 0.0;
  140. coverView.backgroundColor = [UIColor whiteColor];
  141. zoomingQrImageView = [[UIImageView alloc] initWithFrame:qrRect];
  142. zoomingQrImageView.image = qrCodeImage;
  143. zoomingQrImageView.alpha = 0.75;
  144. [coverView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fullScreenQrCodeTapped:)]];
  145. [self.view.superview addSubview:coverView];
  146. [self.view.superview addSubview:zoomingQrImageView];
  147. self.qrImageView.hidden = YES;
  148. [UIView animateWithDuration:0.5 animations:^{
  149. coverView.alpha = 1.0;
  150. zoomingQrImageView.frame = qrTargetRect;
  151. zoomingQrImageView.alpha = 1.0;
  152. }];
  153. prevBrightness = [UIScreen mainScreen].brightness;
  154. [UIScreen mainScreen].brightness = 1.0;
  155. }
  156. - (void)hideFullScreenQrViewAnimated:(BOOL)animated {
  157. if (coverView == nil)
  158. return;
  159. if (animated) {
  160. [UIView animateWithDuration:0.5 animations:^{
  161. coverView.alpha = 0.0;
  162. zoomingQrImageView.frame = qrRect;
  163. zoomingQrImageView.alpha = 0.75;
  164. } completion:^(BOOL finished) {
  165. self.qrImageView.hidden = NO;
  166. [coverView removeFromSuperview];
  167. [zoomingQrImageView removeFromSuperview];
  168. coverView = nil;
  169. zoomingQrImageView = nil;
  170. }];
  171. } else {
  172. self.qrImageView.hidden = NO;
  173. [coverView removeFromSuperview];
  174. [zoomingQrImageView removeFromSuperview];
  175. coverView = nil;
  176. zoomingQrImageView = nil;
  177. }
  178. [UIScreen mainScreen].brightness = prevBrightness;
  179. }
  180. - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
  181. [self hideFullScreenQrViewAnimated:NO];
  182. [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
  183. }
  184. - (NSString*)wrapText:(NSString*)text withFixedWidth:(NSUInteger)width {
  185. NSMutableString *wrappedText = [NSMutableString string];
  186. NSUInteger offset = 0;
  187. while (offset < text.length) {
  188. if (wrappedText.length > 0)
  189. [wrappedText appendString:@"\n"];
  190. NSUInteger rangeLen = MIN(text.length - offset, width);
  191. [wrappedText appendString:[text substringWithRange:NSMakeRange(offset, rangeLen)]];
  192. offset += rangeLen;
  193. }
  194. return wrappedText;
  195. }
  196. #pragma mark - Table view delegate
  197. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  198. {
  199. if (indexPath.section == 1 && indexPath.row == 1) {
  200. [self showFullScreenQrView];
  201. }
  202. }
  203. - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
  204. if (section == 0) {
  205. if (mdmSetup.disableSystemBackups) {
  206. return NSLocalizedString(@"disabled_by_device_policy", nil);
  207. } else {
  208. if (self.phoneBackupSwitch.on) {
  209. return NSLocalizedString(@"backup_include", nil);
  210. } else {
  211. return NSLocalizedString(@"backup_exclude", nil);
  212. }
  213. }
  214. } else if (section == 1) {
  215. return NSLocalizedString(@"backup_footer", nil);
  216. }
  217. return nil;
  218. }
  219. #pragma mark - UIActivityItemSource
  220. - (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController {
  221. return [self activityViewController:activityViewController itemForActivityType:nil];
  222. }
  223. - (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(UIActivityType)activityType {
  224. return self.backupData;
  225. }
  226. - (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(UIActivityType)activityType {
  227. NSString *subject = [NSString stringWithFormat:NSLocalizedString(@"backup_mail_subject", nil), [MyIdentityStore sharedMyIdentityStore].identity];
  228. return subject;
  229. }
  230. @end