ContactDetailsViewController.m 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  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 <Contacts/Contacts.h>
  21. #import <QuartzCore/QuartzCore.h>
  22. #import <MessageUI/MessageUI.h>
  23. #import "ContactDetailsViewController.h"
  24. #import "LinkedContactCell.h"
  25. #import "KeyFingerprintCell.h"
  26. #import "VerificationLevelCell.h"
  27. #import "BlockContactCell.h"
  28. #import "Contact.h"
  29. #import "ContactStore.h"
  30. #import "ConversationsViewController.h"
  31. #import "StatusNavigationBar.h"
  32. #import "ScanIdentityController.h"
  33. #import "CryptoUtils.h"
  34. #import "UIDefines.h"
  35. #import "EditContactViewController.h"
  36. #import "ConversationExporter.h"
  37. #import "EntityManager.h"
  38. #import "AvatarMaker.h"
  39. #import "FullscreenImageViewController.h"
  40. #import "ContactGroupMembershipViewController.h"
  41. #import "ModalNavigationController.h"
  42. #import "BundleUtil.h"
  43. #import "UserSettings.h"
  44. #import "ContactPhotoSender.h"
  45. #import "FeatureMask.h"
  46. #import "ServerConnector.h"
  47. #import "Utils.h"
  48. #import "Threema-Swift.h"
  49. #import "MDMSetup.h"
  50. #import "CopyLabel.h"
  51. #import "ThreemaFramework/ThreemaFramework-swift.h"
  52. #ifdef DEBUG
  53. static const DDLogLevel ddLogLevel = DDLogLevelInfo;
  54. #else
  55. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  56. #endif
  57. @interface ContactDetailsViewController ()
  58. @property NSInteger indexId;
  59. @property NSInteger indexNickname;
  60. @property NSInteger indexLinkedContact;
  61. @property NSInteger indexGroupMembership;
  62. @property NSInteger indexVerificationLevel;
  63. @property NSInteger indexFingerprint;
  64. @property NSInteger numberOfRowsInSection0;
  65. @property BOOL didHideTabBar;
  66. @property NSMutableArray *callNumbers;
  67. @end
  68. @implementation ContactDetailsViewController {
  69. CNContactStore *cnAddressBook;
  70. CNContact *cnContact;
  71. BOOL cnContactViewShowing;
  72. BOOL canExportConversation;
  73. Conversation *conversation;
  74. }
  75. - (void)viewDidLoad {
  76. [super viewDidLoad];
  77. if (![ScanIdentityController canScan])
  78. self.navigationItem.rightBarButtonItem = nil;
  79. self.navigationController.interactivePopGestureRecognizer.enabled = YES;
  80. self.navigationController.interactivePopGestureRecognizer.delegate = nil;
  81. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(colorThemeChanged:) name:kNotificationColorThemeChanged object:nil];
  82. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showProfilePictureChanged:) name:kNotificationShowProfilePictureChanged object:nil];
  83. cnAddressBook = [CNContactStore new];
  84. UITapGestureRecognizer *disclosureTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedHeaderView)];
  85. [_disclosureButton addGestureRecognizer:disclosureTapRecognizer];
  86. _disclosureButton.accessibilityLabel = [BundleUtil localizedStringForKey:@"edit_contact"];
  87. UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedImage)];
  88. [_imageView addGestureRecognizer:tapRecognizer];
  89. _threemaTypeIcon.image = [Utils threemaTypeIcon];
  90. [self setupColors];
  91. }
  92. - (void)setupColors {
  93. [_nameLabel setTextColor:[Colors fontNormal]];
  94. _nameLabel.shadowColor = nil;
  95. [_companyNameLabel setTextColor:[Colors fontLight]];
  96. _companyNameLabel.shadowColor = nil;
  97. UIImage *disclosureImage = [self.disclosureButton.imageView.image imageWithTint:[Colors main]];
  98. [self.disclosureButton setImage:disclosureImage forState:UIControlStateNormal];
  99. if (@available(iOS 11.0, *)) {
  100. _imageView.accessibilityIgnoresInvertColors = true;
  101. _threemaTypeIcon.accessibilityIgnoresInvertColors = true;
  102. }
  103. }
  104. - (void)viewWillAppear:(BOOL)animated {
  105. [super viewWillAppear:animated];
  106. /* did we come back from ABPersonView? If so, the contact data may have changed,
  107. and we need to update */
  108. if (cnContactViewShowing) {
  109. cnContactViewShowing = NO;
  110. [[ContactStore sharedContactStore] updateContact:self.contact];
  111. [(StatusNavigationBar *)self.navigationController.navigationBar showOrHideStatusView];
  112. [Colors updateNavigationBar:self.navigationController.navigationBar];
  113. }
  114. self.view.alpha = 1.0;
  115. [self updateView];
  116. }
  117. - (void)viewDidAppear:(BOOL)animated {
  118. [super viewDidAppear:animated];
  119. if (self.navigationController.navigationBarHidden) {
  120. [self.navigationController setNavigationBarHidden:NO];
  121. }
  122. }
  123. - (void)viewWillLayoutSubviews {
  124. [super viewWillLayoutSubviews];
  125. UIFontDescriptor *fontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleTitle3];
  126. CGFloat size = fontDescriptor.pointSize;
  127. _nameLabel.font = [UIFont boldSystemFontOfSize:size];
  128. }
  129. - (BOOL)shouldAutorotate {
  130. return YES;
  131. }
  132. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  133. if (SYSTEM_IS_IPAD) {
  134. return UIInterfaceOrientationMaskAll;
  135. }
  136. return UIInterfaceOrientationMaskAllButUpsideDown;
  137. }
  138. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  139. if ([segue.identifier isEqualToString:@"EditName"]) {
  140. EditContactViewController *editVc = (EditContactViewController*)segue.destinationViewController;
  141. editVc.contact = self.contact;
  142. }
  143. else if ([segue.identifier isEqualToString:@"ShowPushSetting"]) {
  144. NotificationSettingViewController *notificationSettingViewController = (NotificationSettingViewController *)segue.destinationViewController;
  145. notificationSettingViewController.identity = self.contact.identity;
  146. notificationSettingViewController.isGroup = NO;
  147. notificationSettingViewController.conversation = conversation;
  148. }
  149. }
  150. - (void)updateView {
  151. _scanQrCodeBarButtonItem.accessibilityLabel = [BundleUtil localizedStringForKey:@"scan_identity"];
  152. NSString *name = self.contact.displayName;
  153. self.navigationItem.title = name;
  154. _nameLabel.text = name;
  155. _headerView.accessibilityLabel = _nameLabel.text;
  156. _imageView.image = [[AvatarMaker sharedAvatarMaker] avatarForContact:self.contact size:_imageView.frame.size.width masked:NO];
  157. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  158. _imageView.layer.masksToBounds = YES;
  159. _imageView.layer.cornerRadius = _imageView.bounds.size.width / 2;
  160. _threemaTypeIcon.hidden = [Utils hideThreemaTypeIconForContact:self.contact];
  161. self.companyNameLabel.text = @"";
  162. cnContact = nil;
  163. if (self.contact.cnContactId != nil) {
  164. if (cnAddressBook != nil) {
  165. [cnAddressBook requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
  166. if (granted == YES) {
  167. NSPredicate *predicate = [CNContact predicateForContactsWithIdentifiers:@[self.contact.cnContactId]];
  168. NSError *error;
  169. NSMutableArray *keys = [[NSMutableArray alloc] initWithArray:kCNContactKeys];
  170. [keys addObject:[CNContactViewController descriptorForRequiredKeys]];
  171. NSArray *cnContacts = [cnAddressBook unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
  172. if (error) {
  173. NSLog(@"error fetching contacts %@", error);
  174. } else {
  175. cnContact = cnContacts.firstObject;
  176. [self getPhoneCallNumbers];
  177. dispatch_async(dispatch_get_main_queue(), ^{
  178. self.companyNameLabel.text = cnContact.organizationName;
  179. [self.tableView reloadData];
  180. });
  181. }
  182. }
  183. }];
  184. }
  185. }
  186. CGFloat headerHeight = 0;
  187. if (self.companyNameLabel.text.length == 0) {
  188. self.companyNameLabel.hidden = true;
  189. headerHeight = 275.0;
  190. } else {
  191. self.companyNameLabel.hidden = false;
  192. headerHeight = 300.0;
  193. }
  194. _headerView.frame = CGRectMake(_headerView.frame.origin.x, _headerView.frame.origin.y, _headerView.frame.size.width, headerHeight);
  195. /* show e-mail conversation button only if there is a conversation for this contact */
  196. MDMSetup *mdmSetup = [[MDMSetup alloc] initWithSetup:NO];
  197. EntityManager *entityManager = [[EntityManager alloc] init];
  198. conversation = [entityManager.entityFetcher conversationForContact: self.contact];
  199. if (conversation == nil || [mdmSetup disableExport])
  200. canExportConversation = NO;
  201. else
  202. canExportConversation = YES;
  203. NSInteger i = 0;
  204. _indexId = i++;
  205. if (self.contact.publicNickname.length > 0 && ![self.contact.publicNickname isEqualToString:self.contact.identity]) {
  206. _indexNickname = i++;
  207. } else {
  208. _indexNickname = -1;
  209. }
  210. if (_contact.isGatewayId) {
  211. _indexLinkedContact = -1;
  212. _disclosureButton.hidden = YES;
  213. } else {
  214. _indexLinkedContact = i++;
  215. _disclosureButton.hidden = NO;
  216. }
  217. _indexGroupMembership = i++;
  218. _indexVerificationLevel = i++;
  219. _indexFingerprint = i++;
  220. _numberOfRowsInSection0 = _indexFingerprint + 1;
  221. if (_didHideTabBar) {
  222. [self.tabBarController.tabBar setHidden:NO];
  223. _didHideTabBar = NO;
  224. }
  225. [self.tableView reloadData];
  226. }
  227. - (IBAction)sendMessageAction:(id)sender {
  228. NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
  229. self.contact, kKeyContact,
  230. [NSNumber numberWithBool:YES], kKeyForceCompose,
  231. nil];
  232. [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationShowConversation object:nil userInfo:info];
  233. }
  234. - (IBAction)scanIdentityAction:(id)sender; {
  235. ScanIdentityController *scanController = [[ScanIdentityController alloc] init];
  236. scanController.containingViewController = self;
  237. scanController.expectedIdentity = self.contact.identity;
  238. scanController.popupScanResults = NO;
  239. [scanController startScan];
  240. }
  241. - (void)conversationAction:(id)sender {
  242. UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"include_media_title", nil), kExportConversationMediaSizeLimit] message:nil preferredStyle:UIAlertControllerStyleActionSheet];
  243. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"include_media", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  244. ConversationExporter *exporter = [ConversationExporter exporterOnViewController: self];
  245. [exporter exportConversationForContact:self.contact withMedia: YES];
  246. }]];
  247. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"without_media", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  248. ConversationExporter *exporter = [ConversationExporter exporterOnViewController: self];
  249. [exporter exportConversationForContact:self.contact withMedia: NO];
  250. }]];
  251. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
  252. [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] animated:YES];
  253. }]];
  254. if ([sender isKindOfClass:[UIView class]]) {
  255. UIView *senderView = (UIView *)sender;
  256. actionSheet.popoverPresentationController.sourceRect = senderView.frame;
  257. actionSheet.popoverPresentationController.sourceView = self.view;
  258. }
  259. [[[AppDelegate sharedAppDelegate] currentTopViewController] presentViewController:actionSheet animated:YES completion:nil];
  260. }
  261. - (void)tappedImage {
  262. if ((_contact.contactImage && [UserSettings sharedUserSettings].showProfilePictures) || _contact.imageData) {
  263. UIImage *image = nil;
  264. if (_contact.contactImage && [UserSettings sharedUserSettings].showProfilePictures) {
  265. image = [UIImage imageWithData:_contact.contactImage.data];
  266. } else {
  267. image = [UIImage imageWithData:_contact.imageData];
  268. }
  269. FullscreenImageViewController *imageController = [FullscreenImageViewController controllerForImage:image];
  270. if (SYSTEM_IS_IPAD) {
  271. ModalNavigationController *nav = [[ModalNavigationController alloc] initWithRootViewController:imageController];
  272. nav.showDoneButton = YES;
  273. nav.showFullScreenOnIPad = YES;
  274. [self presentViewController:nav animated:YES completion:nil];
  275. } else {
  276. [self.navigationController pushViewController:imageController animated:YES];
  277. }
  278. } else {
  279. [self tappedHeaderView];
  280. }
  281. }
  282. - (void)tappedHeaderView {
  283. if (self.contact.isGatewayId) {
  284. return;
  285. }
  286. if (cnContact != nil) {
  287. CNContactViewController *personVc = [CNContactViewController viewControllerForContact:cnContact];
  288. personVc.allowsActions = YES;
  289. personVc.allowsEditing = YES;
  290. cnContactViewShowing = YES;
  291. if (self.tabBarController.tabBar.hidden == NO) {
  292. _didHideTabBar = YES;
  293. [self.tabBarController.tabBar setHidden:YES];
  294. }
  295. [(StatusNavigationBar *)self.navigationController.navigationBar hideStatusView];
  296. self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
  297. [self.navigationController pushViewController:personVc animated:YES];
  298. } else {
  299. [self showEditContactVC];
  300. }
  301. }
  302. - (void)startPofilePictureAction {
  303. ContactPhotoSender *sender = [[ContactPhotoSender alloc] init];
  304. [sender startWithImageToMember:_contact onCompletion:^{
  305. [UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"my_profilepicture", nil) message:NSLocalizedString(@"contact_send_profilepicture_success", nil) actionOk:nil];
  306. } onError:^(NSError *err) {
  307. [UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"my_profilepicture", nil) message:NSLocalizedString(@"contact_send_profilepicture_error", nil) actionOk:nil];
  308. }];
  309. [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
  310. }
  311. - (void)startThreemaCallAction {
  312. NSInteger state = [[VoIPCallStateManager shared] currentCallState];
  313. if (state == CallStateIdle) {
  314. [FeatureMask checkFeatureMask:FEATURE_MASK_VOIP forContacts:[NSSet setWithObjects:_contact, nil] onCompletion:^(NSArray *unsupportedContacts) {
  315. if (unsupportedContacts.count == 0) {
  316. [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
  317. [self startVoipCall];
  318. } else {
  319. [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
  320. [UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"call_voip_not_supported_title", nil) message:NSLocalizedString(@"call_voip_not_supported_text", nil) actionOk:nil];
  321. }
  322. }];
  323. } else {
  324. [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
  325. }
  326. }
  327. - (void)startPhoneCallAction {
  328. NSIndexPath *selectedIndexPath = self.tableView.indexPathForSelectedRow;
  329. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  330. if (_contact.cnContactId != nil) {
  331. [cnAddressBook requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
  332. if (granted == YES) {
  333. NSPredicate *predicate = [CNContact predicateForContactsWithIdentifiers:@[_contact.cnContactId]];
  334. NSError *error;
  335. NSArray *cnContacts = [cnAddressBook unifiedContactsMatchingPredicate:predicate keysToFetch:kCNContactKeys error:&error];
  336. if (error) {
  337. NSLog(@"error fetching contacts %@", error);
  338. } else {
  339. CNContact *person = cnContacts.firstObject;
  340. if (person != nil) {
  341. /* get all phone numbers and present in Action Sheet */
  342. UIAlertController *callActionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
  343. _callNumbers = [NSMutableArray array];
  344. for (CNLabeledValue *phone in person.phoneNumbers) {
  345. NSString *label = phone.label;
  346. NSString *localizedLabel = [CNLabeledValue localizedStringForLabel:label];
  347. NSString *number = [phone.value stringValue];
  348. if ([_callNumbers containsObject:number])
  349. continue;
  350. [callActionSheet addAction:[UIAlertAction actionWithTitle:[NSString stringWithFormat:@"%@: %@", localizedLabel, number] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  351. NSUInteger index = [callActionSheet.actions indexOfObject:action];
  352. NSString *number = _callNumbers[index];
  353. [[UIApplication sharedApplication] openURL:[self makeTelUrlForPhone:number] options:@{} completionHandler:nil];
  354. }]];
  355. [_callNumbers addObject:number];
  356. }
  357. // DDLogVerbose(@"Phone numbers: %@", _callNumbers);
  358. [callActionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
  359. }]];
  360. if (_callNumbers.count == 0) {
  361. [UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"call_voip_not_supported_title", nil) message:NSLocalizedString(@"call_voip_not_supported_text", nil) actionOk:nil];
  362. } else if (_callNumbers.count == 1) {
  363. [[UIApplication sharedApplication] openURL:[self makeTelUrlForPhone:_callNumbers[0]] options:@{} completionHandler:nil];
  364. } else if (_callNumbers.count > 1) {
  365. if (SYSTEM_IS_IPAD) {
  366. UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedIndexPath];
  367. callActionSheet.popoverPresentationController.sourceView = cell.contentView;
  368. callActionSheet.popoverPresentationController.sourceRect = cell.contentView.bounds;
  369. }
  370. [[[AppDelegate sharedAppDelegate] currentTopViewController] presentViewController:callActionSheet animated:YES completion:nil];
  371. }
  372. }
  373. }
  374. }
  375. }];
  376. }
  377. }
  378. - (void)startVoipCall {
  379. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  380. if ([ServerConnector sharedServerConnector].connectionState == ConnectionStateLoggedIn) {
  381. VoIPCallUserAction *action = [[VoIPCallUserAction alloc] initWithAction:ActionCall contact:_contact completion:nil];
  382. [[VoIPCallStateManager shared] processUserAction:action];
  383. } else {
  384. // Alert no internet connection
  385. NSString *title = NSLocalizedString(@"cannot_connect_title", nil);
  386. NSString *message = NSLocalizedString(@"cannot_connect_message", nil);
  387. [UIAlertTemplate showAlertWithOwner:self title:title message:message actionOk:^(UIAlertAction * _Nonnull okAction) {
  388. [self.extensionContext completeRequestReturningItems:@[] completionHandler:^(BOOL expired) {
  389. }];
  390. }];
  391. }
  392. }
  393. - (NSURL*)makeTelUrlForPhone:(NSString*)phoneNumber {
  394. return [NSURL URLWithString:[NSString stringWithFormat:@"tel:%@", [phoneNumber stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]]];
  395. }
  396. - (void)getPhoneCallNumbers {
  397. if (cnContact != nil) {
  398. _callNumbers = nil;
  399. _callNumbers = [NSMutableArray array];
  400. for (CNLabeledValue *phone in cnContact.phoneNumbers) {
  401. NSString *number = [phone.value stringValue];
  402. if ([_callNumbers containsObject:number])
  403. continue;
  404. [_callNumbers addObject:number];
  405. }
  406. }
  407. }
  408. - (BOOL)showPhoneCallButton {
  409. if (cnContact != nil) {
  410. if (_callNumbers.count == 0) {
  411. return NO;
  412. }
  413. return YES;
  414. }
  415. return NO;
  416. }
  417. #pragma mark - Table view data source
  418. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  419. {
  420. if (!_contact.isGatewayId && !_contact.isEchoEcho && [UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts) {
  421. return 4;
  422. }
  423. return 3;
  424. }
  425. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  426. {
  427. if (section == 0) {
  428. return _numberOfRowsInSection0;
  429. } else if (section == 1) {
  430. if (_hideActionButtons) {
  431. return 0;
  432. }
  433. NSInteger numRows = [UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1 ? 2 : 1;
  434. if ([ScanIdentityController canScan])
  435. numRows++;
  436. if (canExportConversation)
  437. numRows++;
  438. if (!_contact.isGatewayId && !_contact.isEchoEcho && ([UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureAll || ([UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts && [[UserSettings sharedUserSettings].profilePictureContactList containsObject:_contact.identity])))
  439. numRows++;
  440. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton])
  441. numRows++;
  442. return numRows;
  443. } else if (section == 2) {
  444. if (!_contact.isGatewayId && !_contact.isEchoEcho && [UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts) {
  445. return 1;
  446. } else {
  447. return 2;
  448. }
  449. } else {
  450. return 2;
  451. }
  452. }
  453. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  454. switch (indexPath.section) {
  455. case 0:
  456. if (indexPath.row == _indexId) {
  457. UITableViewCell *identityCell = [tableView dequeueReusableCellWithIdentifier:@"IdentityCell"];
  458. ((UILabel*)[identityCell viewWithTag:100]).text = self.contact.identity;
  459. ((UILabel*)[identityCell viewWithTag:100]).isAccessibilityElement = false;
  460. return identityCell;
  461. } else if (indexPath.row == _indexFingerprint) {
  462. KeyFingerprintCell *kfc = [tableView dequeueReusableCellWithIdentifier:@"KeyFingerprintCell"];
  463. kfc.fingerprintLabel.text = [CryptoUtils fingerprintForPublicKey:self.contact.publicKey];
  464. return kfc;
  465. } else if (indexPath.row == _indexVerificationLevel) {
  466. VerificationLevelCell *vlc = [tableView dequeueReusableCellWithIdentifier:@"VerificationLevelCell"];
  467. vlc.contact = self.contact;
  468. vlc.accessibilityTraits = UIAccessibilityTraitButton;
  469. return vlc;
  470. } else if (indexPath.row == _indexNickname) {
  471. UITableViewCell *publicNicknameCell = [tableView dequeueReusableCellWithIdentifier:@"PublicNicknameCell"];
  472. ((UILabel*)[publicNicknameCell viewWithTag:101]).text = self.contact.publicNickname;
  473. return publicNicknameCell;
  474. } else if (indexPath.row == _indexGroupMembership) {
  475. UITableViewCell *groupMembershipCell = [tableView dequeueReusableCellWithIdentifier:@"GroupMembershipCell"];
  476. ((UILabel*)[groupMembershipCell viewWithTag:102]).text = [NSString stringWithFormat:@"%lu", (unsigned long)[self.contact.groupConversations count]];
  477. groupMembershipCell.accessibilityTraits = UIAccessibilityTraitButton;
  478. return groupMembershipCell;
  479. } else if (indexPath.row == _indexLinkedContact) {
  480. LinkedContactCell *lcc = [tableView dequeueReusableCellWithIdentifier:@"LinkedContactCell"];
  481. lcc.accessibilityTraits = UIAccessibilityTraitButton;
  482. if (cnContact != nil) {
  483. lcc.displayNameLabel.text = [CNContactFormatter stringFromContact:cnContact style:CNContactFormatterStyleFullName];
  484. if (lcc.displayNameLabel.text.length == 0) {
  485. if (cnContact.emailAddresses) {
  486. if (cnContact.emailAddresses.count > 0) {
  487. lcc.displayNameLabel.text = ((CNLabeledValue *)cnContact.emailAddresses.firstObject).value;
  488. }
  489. }
  490. }
  491. } else {
  492. lcc.displayNameLabel.text = [BundleUtil localizedStringForKey:@"(none)"];
  493. }
  494. return lcc;
  495. }
  496. break;
  497. case 1: {
  498. NSString *cellIdentifier;
  499. if (indexPath.row == 0) {
  500. cellIdentifier = @"SendMessageCell";
  501. }
  502. else if (indexPath.row == 1) {
  503. if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
  504. cellIdentifier = @"ThreemaCallCell";
  505. } else {
  506. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  507. cellIdentifier = @"PhoneCallCell";
  508. }
  509. else if (canExportConversation) {
  510. cellIdentifier = @"ExportConversationCell";
  511. }
  512. else if ([ScanIdentityController canScan]) {
  513. cellIdentifier = @"ScanIDCell";
  514. }
  515. else {
  516. cellIdentifier = @"SendProfilePictureCell";
  517. }
  518. }
  519. }
  520. else if (indexPath.row == 2) {
  521. if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
  522. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  523. cellIdentifier = @"PhoneCallCell";
  524. }
  525. else if (canExportConversation) {
  526. cellIdentifier = @"ExportConversationCell";
  527. }
  528. else if ([ScanIdentityController canScan]) {
  529. cellIdentifier = @"ScanIDCell";
  530. }
  531. else {
  532. cellIdentifier = @"SendProfilePictureCell";
  533. }
  534. } else {
  535. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  536. if (canExportConversation) {
  537. cellIdentifier = @"ExportConversationCell";
  538. }
  539. else if (canExportConversation && [ScanIdentityController canScan]) {
  540. cellIdentifier = @"ScanIDCell";
  541. }
  542. else {
  543. cellIdentifier = @"SendProfilePictureCell";
  544. }
  545. } else {
  546. if (canExportConversation && [ScanIdentityController canScan]) {
  547. cellIdentifier = @"ScanIDCell";
  548. } else {
  549. cellIdentifier = @"SendProfilePictureCell";
  550. }
  551. }
  552. }
  553. } else if (indexPath.row == 3) {
  554. if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
  555. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  556. if (canExportConversation) {
  557. cellIdentifier = @"ExportConversationCell";
  558. }
  559. else if ([ScanIdentityController canScan]) {
  560. cellIdentifier = @"ScanIDCell";
  561. }
  562. else {
  563. cellIdentifier = @"SendProfilePictureCell";
  564. }
  565. } else {
  566. if (canExportConversation && [ScanIdentityController canScan]) {
  567. cellIdentifier = @"ScanIDCell";
  568. } else {
  569. cellIdentifier = @"SendProfilePictureCell";
  570. }
  571. }
  572. } else {
  573. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  574. if (canExportConversation && [ScanIdentityController canScan]) {
  575. cellIdentifier = @"ScanIDCell";
  576. }
  577. else {
  578. cellIdentifier = @"SendProfilePictureCell";
  579. }
  580. } else {
  581. cellIdentifier = @"SendProfilePictureCell";
  582. }
  583. }
  584. }
  585. else if (indexPath.row == 4) {
  586. if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
  587. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  588. if (canExportConversation && [ScanIdentityController canScan]) {
  589. cellIdentifier = @"ScanIDCell";
  590. } else {
  591. cellIdentifier = @"SendProfilePictureCell";
  592. }
  593. } else {
  594. cellIdentifier = @"SendProfilePictureCell";
  595. }
  596. } else {
  597. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  598. cellIdentifier = @"SendProfilePictureCell";
  599. }
  600. }
  601. } else if (indexPath.row == 5) {
  602. cellIdentifier = @"SendProfilePictureCell";
  603. }
  604. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
  605. cell.accessibilityTraits = UIAccessibilityTraitButton;
  606. return cell;
  607. }
  608. case 2: {
  609. if (!_contact.isGatewayId && !_contact.isEchoEcho && [UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts) {
  610. ProfilePictureRecipientCell *profilePictureRecipientCell = [tableView dequeueReusableCellWithIdentifier:@"ProfilePictureRecipientCell"];
  611. profilePictureRecipientCell.identity = self.contact.identity;
  612. profilePictureRecipientCell.delegate = self;
  613. return profilePictureRecipientCell;
  614. } else {
  615. switch (indexPath.row) {
  616. case 0: {
  617. UITableViewCell *pushSettingCell = [tableView dequeueReusableCellWithIdentifier:@"PushSettingCell"];
  618. pushSettingCell.textLabel.text = NSLocalizedString(@"pushSetting_title", @"");
  619. return pushSettingCell;
  620. }
  621. case 1: {
  622. BlockContactCell *bcc = [tableView dequeueReusableCellWithIdentifier:@"BlockCell"];
  623. bcc.identity = self.contact.identity;
  624. return bcc;
  625. }
  626. }
  627. }
  628. break;
  629. }
  630. case 3: {
  631. switch (indexPath.row) {
  632. case 0: {
  633. UITableViewCell *pushSettingCell = [tableView dequeueReusableCellWithIdentifier:@"PushSettingCell"];
  634. pushSettingCell.textLabel.text = NSLocalizedString(@"pushSetting_title", @"");
  635. return pushSettingCell;
  636. }
  637. case 1: {
  638. BlockContactCell *bcc = [tableView dequeueReusableCellWithIdentifier:@"BlockCell"];
  639. bcc.identity = self.contact.identity;
  640. return bcc;
  641. }
  642. }
  643. break;
  644. }
  645. }
  646. return nil;
  647. }
  648. - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
  649. if (section == 2 && !_contact.isGatewayId && !_contact.isEchoEcho && [UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts) {
  650. if ([[UserSettings sharedUserSettings].profilePictureContactList containsObject:_contact.identity]) {
  651. return NSLocalizedString(@"contact_added_to_profilepicture_list", nil);
  652. } else {
  653. return NSLocalizedString(@"contact_removed_from_profilepicture_list", nil);
  654. }
  655. }
  656. return nil;
  657. }
  658. #pragma mark - Table view delegate
  659. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
  660. if (indexPath.section == 0) {
  661. [Colors updateTableViewCellBackground:cell];
  662. // handle custom table cells
  663. [Colors setTextColor:[Colors fontNormal] inView:cell.contentView];
  664. if (indexPath.row == _indexId) {
  665. ((UILabel*)[cell viewWithTag:100]).textColor = [Colors fontLight];
  666. } else if (indexPath.row == _indexFingerprint) {
  667. KeyFingerprintCell *kfc = (KeyFingerprintCell *)cell;
  668. kfc.fingerprintLabel.textColor = [Colors fontLight];
  669. } else if (indexPath.row == _indexNickname) {
  670. ((UILabel*)[cell viewWithTag:101]).textColor = [Colors fontLight];
  671. } else if (indexPath.row == _indexGroupMembership) {
  672. ((UILabel*)[cell viewWithTag:102]).textColor = [Colors fontLight];
  673. } else if (indexPath.row == _indexLinkedContact) {
  674. LinkedContactCell *lcc = (LinkedContactCell *)cell;
  675. lcc.displayNameLabel.textColor = [Colors fontLight];
  676. }
  677. } else {
  678. [Colors updateTableViewCell:cell];
  679. }
  680. }
  681. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  682. switch (indexPath.section) {
  683. case 0:
  684. if (indexPath.row == _indexLinkedContact) {
  685. UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
  686. [self linkNewContact: cell];
  687. } else if (indexPath.row == _indexGroupMembership) {
  688. ContactGroupMembershipViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"contactGroupMembershipViewController"];
  689. vc.groupContact = self.contact;
  690. [self.navigationController pushViewController:vc animated:YES];
  691. }
  692. break;
  693. case 1:
  694. switch (indexPath.row) {
  695. case 0:
  696. [self sendMessageAction:self];
  697. break;
  698. case 1:
  699. if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
  700. [self startThreemaCallAction];
  701. } else {
  702. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  703. [self startPhoneCallAction];
  704. }
  705. else if (canExportConversation) {
  706. UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
  707. [self conversationAction:cell];
  708. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  709. }
  710. else if ([ScanIdentityController canScan]) {
  711. [self scanIdentityAction:nil];
  712. }
  713. else {
  714. [self startPofilePictureAction];
  715. }
  716. }
  717. break;
  718. case 2:
  719. if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
  720. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  721. [self startPhoneCallAction];
  722. }
  723. else if (canExportConversation) {
  724. UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
  725. [self conversationAction:cell];
  726. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  727. }
  728. else if ([ScanIdentityController canScan]) {
  729. [self scanIdentityAction:nil];
  730. }
  731. else {
  732. [self startPofilePictureAction];
  733. }
  734. } else {
  735. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  736. if (canExportConversation) {
  737. UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
  738. [self conversationAction:cell];
  739. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  740. }
  741. else if (canExportConversation && [ScanIdentityController canScan]) {
  742. [self scanIdentityAction:nil];
  743. }
  744. else {
  745. [self startPofilePictureAction];
  746. }
  747. }
  748. else {
  749. if (canExportConversation && [ScanIdentityController canScan]) {
  750. [self scanIdentityAction:nil];
  751. }
  752. else {
  753. [self startPofilePictureAction];
  754. }
  755. }
  756. }
  757. break;
  758. case 3: {
  759. if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
  760. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  761. if (canExportConversation) {
  762. UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
  763. [self conversationAction:cell];
  764. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  765. }
  766. else if ([ScanIdentityController canScan]) {
  767. [self scanIdentityAction:nil];
  768. }
  769. else {
  770. [self startPofilePictureAction];
  771. }
  772. } else {
  773. if (canExportConversation && [ScanIdentityController canScan]) {
  774. [self scanIdentityAction:nil];
  775. }
  776. else {
  777. [self startPofilePictureAction];
  778. }
  779. }
  780. } else {
  781. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  782. if (canExportConversation && [ScanIdentityController canScan]) {
  783. [self scanIdentityAction:nil];
  784. }
  785. else {
  786. [self startPofilePictureAction];
  787. }
  788. } else {
  789. [self startPofilePictureAction];
  790. }
  791. }
  792. break;
  793. }
  794. case 4: {
  795. if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
  796. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  797. if (canExportConversation && [ScanIdentityController canScan]) {
  798. [self scanIdentityAction:nil];
  799. } else {
  800. [self startPofilePictureAction];
  801. }
  802. } else {
  803. [self startPofilePictureAction];
  804. }
  805. } else {
  806. if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
  807. [self startPofilePictureAction];
  808. }
  809. }
  810. break;
  811. }
  812. case 5: {
  813. [self startPofilePictureAction];
  814. break;
  815. }
  816. }
  817. break;
  818. }
  819. }
  820. - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
  821. switch (indexPath.section) {
  822. case 0:
  823. if (indexPath.row == _indexVerificationLevel) {
  824. [self performSegueWithIdentifier:@"VerificationSegue" sender:nil];
  825. }
  826. break;
  827. default:
  828. break;
  829. }
  830. }
  831. - (void)showEditContactVC {
  832. EditContactViewController *editVc = [self.storyboard instantiateViewControllerWithIdentifier:@"EditContactViewController"];
  833. editVc.contact = self.contact;
  834. [self.navigationController pushViewController:editVc animated:YES];
  835. }
  836. - (void)linkNewContact:(UIView *)view {
  837. /* Already linked? If so, give user a choice to unlink */
  838. if (cnContact != nil) {
  839. UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
  840. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"unlink_contact", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction * action) {
  841. /* unlink contact */
  842. [[ContactStore sharedContactStore] unlinkContact:self.contact];
  843. [self updateView];
  844. }]];
  845. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"choose_new_contact", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  846. [self linkNewContactCheckAuthorization];
  847. }]];
  848. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  849. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  850. }]];
  851. actionSheet.popoverPresentationController.sourceRect = view.frame;
  852. actionSheet.popoverPresentationController.sourceView = self.view;
  853. [self presentViewController:actionSheet animated:YES completion:nil];
  854. } else {
  855. [self linkNewContactCheckAuthorization];
  856. }
  857. }
  858. - (void)linkNewContactCheckAuthorization {
  859. if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] != CNAuthorizationStatusAuthorized) {
  860. [cnAddressBook requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
  861. if (granted == YES) {
  862. DDLogInfo(@"Address book access has been granted");
  863. dispatch_async(dispatch_get_main_queue(), ^{
  864. [self linkNewContactPick];
  865. });
  866. } else {
  867. DDLogInfo(@"Address book access has NOT been granted: %@", error);
  868. dispatch_async(dispatch_get_main_queue(), ^{
  869. UIAlertController *accessAlert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"no_contacts_permission_title", nil) message:NSLocalizedString(@"no_contacts_permission_message", nil) preferredStyle:UIAlertControllerStyleAlert];
  870. if (self.contact.cnContactId != nil) {
  871. [accessAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"unlink_contact", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  872. [[ContactStore sharedContactStore] unlinkContact:self.contact];
  873. [self updateView];
  874. }]];
  875. }
  876. [accessAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ok", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  877. }]];
  878. [self presentViewController:accessAlert animated:YES completion:nil];
  879. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  880. });
  881. }
  882. }];
  883. return;
  884. } else {
  885. [self linkNewContactPick];
  886. }
  887. }
  888. - (void)linkNewContactPick {
  889. CNContactPickerViewController *picker = [[CNContactPickerViewController alloc] init];
  890. picker.delegate = self;
  891. picker.modalPresentationStyle = UIModalPresentationFormSheet;
  892. [self presentViewController:picker animated:YES completion:nil];
  893. }
  894. #pragma mark - People picker delegate
  895. -(void) contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{
  896. [(StatusNavigationBar *)self.navigationController.navigationBar showOrHideStatusView];
  897. [[ContactStore sharedContactStore] linkContact:self.contact toCnContactId:contact.identifier];
  898. [self dismissViewControllerAnimated:YES completion:nil];
  899. [self updateView];
  900. }
  901. - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty {
  902. [self dismissViewControllerAnimated:YES completion:nil];
  903. }
  904. -(void)contactPickerDidCancel:(CNContactPickerViewController *)picker {
  905. [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
  906. [(StatusNavigationBar *)self.navigationController.navigationBar showOrHideStatusView];
  907. [self dismissViewControllerAnimated:YES completion:nil];
  908. }
  909. #pragma mark - ProfilePictureRecipientCell delegate
  910. - (void)valueChanged:(ProfilePictureRecipientCell *)cell {
  911. NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)];
  912. [self.tableView beginUpdates];
  913. [self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
  914. [self.tableView endUpdates];
  915. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
  916. [self.tableView reloadData];
  917. });
  918. }
  919. # pragma mark - preview actions
  920. - (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
  921. NSString *sendMessageTitle = NSLocalizedString(@"send_message", nil);
  922. UIPreviewAction *sendMessageAction = [UIPreviewAction actionWithTitle:sendMessageTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
  923. [self sendMessageAction:self];
  924. }];
  925. NSString *scanQrCodeTitle = NSLocalizedString(@"scan_qr", nil);
  926. UIPreviewAction *scanQrCodeAction = [UIPreviewAction actionWithTitle:scanQrCodeTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
  927. // we need to present contact details first and present qr scanner on top of that
  928. [_delegate presentContactDetails:self onCompletion:^(ContactDetailsViewController *contactsDetailsViewController) {
  929. [contactsDetailsViewController scanIdentityAction:nil];
  930. }];
  931. }];
  932. return @[sendMessageAction, scanQrCodeAction];
  933. }
  934. #pragma mark - Notifications
  935. - (void)colorThemeChanged:(NSNotification*)notification {
  936. [self setupColors];
  937. }
  938. - (void)showProfilePictureChanged:(NSNotification*)notification {
  939. [self updateView];
  940. }
  941. @end