// _____ _
// |_ _| |_ _ _ ___ ___ _ __ __ _
// | | | ' \| '_/ -_) -_) ' \/ _` |_
// |_| |_||_|_| \___\___|_|_|_\__,_(_)
//
// Threema iOS Client
// Copyright (c) 2012-2020 Threema GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#import
#import
#import
#import "ContactDetailsViewController.h"
#import "LinkedContactCell.h"
#import "KeyFingerprintCell.h"
#import "VerificationLevelCell.h"
#import "BlockContactCell.h"
#import "Contact.h"
#import "ContactStore.h"
#import "ConversationsViewController.h"
#import "StatusNavigationBar.h"
#import "ScanIdentityController.h"
#import "CryptoUtils.h"
#import "UIDefines.h"
#import "EditContactViewController.h"
#import "ConversationExporter.h"
#import "EntityManager.h"
#import "AvatarMaker.h"
#import "FullscreenImageViewController.h"
#import "ContactGroupMembershipViewController.h"
#import "ModalNavigationController.h"
#import "BundleUtil.h"
#import "UserSettings.h"
#import "ContactPhotoSender.h"
#import "FeatureMask.h"
#import "ServerConnector.h"
#import "Utils.h"
#import "Threema-Swift.h"
#import "MDMSetup.h"
#import "CopyLabel.h"
#import "ThreemaFramework/ThreemaFramework-swift.h"
#ifdef DEBUG
static const DDLogLevel ddLogLevel = DDLogLevelInfo;
#else
static const DDLogLevel ddLogLevel = DDLogLevelWarning;
#endif
@interface ContactDetailsViewController ()
@property NSInteger indexId;
@property NSInteger indexNickname;
@property NSInteger indexLinkedContact;
@property NSInteger indexGroupMembership;
@property NSInteger indexVerificationLevel;
@property NSInteger indexFingerprint;
@property NSInteger numberOfRowsInSection0;
@property BOOL didHideTabBar;
@property NSMutableArray *callNumbers;
@end
@implementation ContactDetailsViewController {
CNContactStore *cnAddressBook;
CNContact *cnContact;
BOOL cnContactViewShowing;
BOOL canExportConversation;
Conversation *conversation;
}
- (void)viewDidLoad {
[super viewDidLoad];
if (![ScanIdentityController canScan])
self.navigationItem.rightBarButtonItem = nil;
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
self.navigationController.interactivePopGestureRecognizer.delegate = nil;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(colorThemeChanged:) name:kNotificationColorThemeChanged object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showProfilePictureChanged:) name:kNotificationShowProfilePictureChanged object:nil];
cnAddressBook = [CNContactStore new];
UITapGestureRecognizer *disclosureTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedHeaderView)];
[_disclosureButton addGestureRecognizer:disclosureTapRecognizer];
_disclosureButton.accessibilityLabel = [BundleUtil localizedStringForKey:@"edit_contact"];
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedImage)];
[_imageView addGestureRecognizer:tapRecognizer];
_threemaTypeIcon.image = [Utils threemaTypeIcon];
[self setupColors];
}
- (void)setupColors {
[_nameLabel setTextColor:[Colors fontNormal]];
_nameLabel.shadowColor = nil;
[_companyNameLabel setTextColor:[Colors fontLight]];
_companyNameLabel.shadowColor = nil;
UIImage *disclosureImage = [self.disclosureButton.imageView.image imageWithTint:[Colors main]];
[self.disclosureButton setImage:disclosureImage forState:UIControlStateNormal];
if (@available(iOS 11.0, *)) {
_imageView.accessibilityIgnoresInvertColors = true;
_threemaTypeIcon.accessibilityIgnoresInvertColors = true;
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
/* did we come back from ABPersonView? If so, the contact data may have changed,
and we need to update */
if (cnContactViewShowing) {
cnContactViewShowing = NO;
[[ContactStore sharedContactStore] updateContact:self.contact];
[(StatusNavigationBar *)self.navigationController.navigationBar showOrHideStatusView];
[Colors updateNavigationBar:self.navigationController.navigationBar];
}
self.view.alpha = 1.0;
[self updateView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.navigationController.navigationBarHidden) {
[self.navigationController setNavigationBarHidden:NO];
}
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
UIFontDescriptor *fontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleTitle3];
CGFloat size = fontDescriptor.pointSize;
_nameLabel.font = [UIFont boldSystemFontOfSize:size];
}
- (BOOL)shouldAutorotate {
return YES;
}
-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
if (SYSTEM_IS_IPAD) {
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"EditName"]) {
EditContactViewController *editVc = (EditContactViewController*)segue.destinationViewController;
editVc.contact = self.contact;
}
else if ([segue.identifier isEqualToString:@"ShowPushSetting"]) {
NotificationSettingViewController *notificationSettingViewController = (NotificationSettingViewController *)segue.destinationViewController;
notificationSettingViewController.identity = self.contact.identity;
notificationSettingViewController.isGroup = NO;
notificationSettingViewController.conversation = conversation;
}
}
- (void)updateView {
_scanQrCodeBarButtonItem.accessibilityLabel = [BundleUtil localizedStringForKey:@"scan_identity"];
NSString *name = self.contact.displayName;
self.navigationItem.title = name;
_nameLabel.text = name;
_headerView.accessibilityLabel = _nameLabel.text;
_imageView.image = [[AvatarMaker sharedAvatarMaker] avatarForContact:self.contact size:_imageView.frame.size.width masked:NO];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
_imageView.layer.masksToBounds = YES;
_imageView.layer.cornerRadius = _imageView.bounds.size.width / 2;
_threemaTypeIcon.hidden = [Utils hideThreemaTypeIconForContact:self.contact];
self.companyNameLabel.text = @"";
cnContact = nil;
if (self.contact.cnContactId != nil) {
if (cnAddressBook != nil) {
[cnAddressBook requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted == YES) {
NSPredicate *predicate = [CNContact predicateForContactsWithIdentifiers:@[self.contact.cnContactId]];
NSError *error;
NSMutableArray *keys = [[NSMutableArray alloc] initWithArray:kCNContactKeys];
[keys addObject:[CNContactViewController descriptorForRequiredKeys]];
NSArray *cnContacts = [cnAddressBook unifiedContactsMatchingPredicate:predicate keysToFetch:keys error:&error];
if (error) {
NSLog(@"error fetching contacts %@", error);
} else {
cnContact = cnContacts.firstObject;
[self getPhoneCallNumbers];
dispatch_async(dispatch_get_main_queue(), ^{
self.companyNameLabel.text = cnContact.organizationName;
[self.tableView reloadData];
});
}
}
}];
}
}
CGFloat headerHeight = 0;
if (self.companyNameLabel.text.length == 0) {
self.companyNameLabel.hidden = true;
headerHeight = 275.0;
} else {
self.companyNameLabel.hidden = false;
headerHeight = 300.0;
}
_headerView.frame = CGRectMake(_headerView.frame.origin.x, _headerView.frame.origin.y, _headerView.frame.size.width, headerHeight);
/* show e-mail conversation button only if there is a conversation for this contact */
MDMSetup *mdmSetup = [[MDMSetup alloc] initWithSetup:NO];
EntityManager *entityManager = [[EntityManager alloc] init];
conversation = [entityManager.entityFetcher conversationForContact: self.contact];
if (conversation == nil || [mdmSetup disableExport])
canExportConversation = NO;
else
canExportConversation = YES;
NSInteger i = 0;
_indexId = i++;
if (self.contact.publicNickname.length > 0 && ![self.contact.publicNickname isEqualToString:self.contact.identity]) {
_indexNickname = i++;
} else {
_indexNickname = -1;
}
if (_contact.isGatewayId) {
_indexLinkedContact = -1;
_disclosureButton.hidden = YES;
} else {
_indexLinkedContact = i++;
_disclosureButton.hidden = NO;
}
_indexGroupMembership = i++;
_indexVerificationLevel = i++;
_indexFingerprint = i++;
_numberOfRowsInSection0 = _indexFingerprint + 1;
if (_didHideTabBar) {
[self.tabBarController.tabBar setHidden:NO];
_didHideTabBar = NO;
}
[self.tableView reloadData];
}
- (IBAction)sendMessageAction:(id)sender {
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
self.contact, kKeyContact,
[NSNumber numberWithBool:YES], kKeyForceCompose,
nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationShowConversation object:nil userInfo:info];
}
- (IBAction)scanIdentityAction:(id)sender; {
ScanIdentityController *scanController = [[ScanIdentityController alloc] init];
scanController.containingViewController = self;
scanController.expectedIdentity = self.contact.identity;
scanController.popupScanResults = NO;
[scanController startScan];
}
- (void)conversationAction:(id)sender {
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"include_media_title", nil), kExportConversationMediaSizeLimit] message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"include_media", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
ConversationExporter *exporter = [ConversationExporter exporterOnViewController: self];
[exporter exportConversationForContact:self.contact withMedia: YES];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"without_media", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
ConversationExporter *exporter = [ConversationExporter exporterOnViewController: self];
[exporter exportConversationForContact:self.contact withMedia: NO];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
[self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] animated:YES];
}]];
if ([sender isKindOfClass:[UIView class]]) {
UIView *senderView = (UIView *)sender;
actionSheet.popoverPresentationController.sourceRect = senderView.frame;
actionSheet.popoverPresentationController.sourceView = self.view;
}
[[[AppDelegate sharedAppDelegate] currentTopViewController] presentViewController:actionSheet animated:YES completion:nil];
}
- (void)tappedImage {
if ((_contact.contactImage && [UserSettings sharedUserSettings].showProfilePictures) || _contact.imageData) {
UIImage *image = nil;
if (_contact.contactImage && [UserSettings sharedUserSettings].showProfilePictures) {
image = [UIImage imageWithData:_contact.contactImage.data];
} else {
image = [UIImage imageWithData:_contact.imageData];
}
FullscreenImageViewController *imageController = [FullscreenImageViewController controllerForImage:image];
if (SYSTEM_IS_IPAD) {
ModalNavigationController *nav = [[ModalNavigationController alloc] initWithRootViewController:imageController];
nav.showDoneButton = YES;
nav.showFullScreenOnIPad = YES;
[self presentViewController:nav animated:YES completion:nil];
} else {
[self.navigationController pushViewController:imageController animated:YES];
}
} else {
[self tappedHeaderView];
}
}
- (void)tappedHeaderView {
if (self.contact.isGatewayId) {
return;
}
if (cnContact != nil) {
CNContactViewController *personVc = [CNContactViewController viewControllerForContact:cnContact];
personVc.allowsActions = YES;
personVc.allowsEditing = YES;
cnContactViewShowing = YES;
if (self.tabBarController.tabBar.hidden == NO) {
_didHideTabBar = YES;
[self.tabBarController.tabBar setHidden:YES];
}
[(StatusNavigationBar *)self.navigationController.navigationBar hideStatusView];
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
[self.navigationController pushViewController:personVc animated:YES];
} else {
[self showEditContactVC];
}
}
- (void)startPofilePictureAction {
ContactPhotoSender *sender = [[ContactPhotoSender alloc] init];
[sender startWithImageToMember:_contact onCompletion:^{
[UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"my_profilepicture", nil) message:NSLocalizedString(@"contact_send_profilepicture_success", nil) actionOk:nil];
} onError:^(NSError *err) {
[UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"my_profilepicture", nil) message:NSLocalizedString(@"contact_send_profilepicture_error", nil) actionOk:nil];
}];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
- (void)startThreemaCallAction {
NSInteger state = [[VoIPCallStateManager shared] currentCallState];
if (state == CallStateIdle) {
[FeatureMask checkFeatureMask:FEATURE_MASK_VOIP forContacts:[NSSet setWithObjects:_contact, nil] onCompletion:^(NSArray *unsupportedContacts) {
if (unsupportedContacts.count == 0) {
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
[self startVoipCall];
} else {
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
[UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"call_voip_not_supported_title", nil) message:NSLocalizedString(@"call_voip_not_supported_text", nil) actionOk:nil];
}
}];
} else {
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
}
- (void)startPhoneCallAction {
NSIndexPath *selectedIndexPath = self.tableView.indexPathForSelectedRow;
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
if (_contact.cnContactId != nil) {
[cnAddressBook requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted == YES) {
NSPredicate *predicate = [CNContact predicateForContactsWithIdentifiers:@[_contact.cnContactId]];
NSError *error;
NSArray *cnContacts = [cnAddressBook unifiedContactsMatchingPredicate:predicate keysToFetch:kCNContactKeys error:&error];
if (error) {
NSLog(@"error fetching contacts %@", error);
} else {
CNContact *person = cnContacts.firstObject;
if (person != nil) {
/* get all phone numbers and present in Action Sheet */
UIAlertController *callActionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
_callNumbers = [NSMutableArray array];
for (CNLabeledValue *phone in person.phoneNumbers) {
NSString *label = phone.label;
NSString *localizedLabel = [CNLabeledValue localizedStringForLabel:label];
NSString *number = [phone.value stringValue];
if ([_callNumbers containsObject:number])
continue;
[callActionSheet addAction:[UIAlertAction actionWithTitle:[NSString stringWithFormat:@"%@: %@", localizedLabel, number] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
NSUInteger index = [callActionSheet.actions indexOfObject:action];
NSString *number = _callNumbers[index];
[[UIApplication sharedApplication] openURL:[self makeTelUrlForPhone:number] options:@{} completionHandler:nil];
}]];
[_callNumbers addObject:number];
}
// DDLogVerbose(@"Phone numbers: %@", _callNumbers);
[callActionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
}]];
if (_callNumbers.count == 0) {
[UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"call_voip_not_supported_title", nil) message:NSLocalizedString(@"call_voip_not_supported_text", nil) actionOk:nil];
} else if (_callNumbers.count == 1) {
[[UIApplication sharedApplication] openURL:[self makeTelUrlForPhone:_callNumbers[0]] options:@{} completionHandler:nil];
} else if (_callNumbers.count > 1) {
if (SYSTEM_IS_IPAD) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectedIndexPath];
callActionSheet.popoverPresentationController.sourceView = cell.contentView;
callActionSheet.popoverPresentationController.sourceRect = cell.contentView.bounds;
}
[[[AppDelegate sharedAppDelegate] currentTopViewController] presentViewController:callActionSheet animated:YES completion:nil];
}
}
}
}
}];
}
}
- (void)startVoipCall {
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
if ([ServerConnector sharedServerConnector].connectionState == ConnectionStateLoggedIn) {
VoIPCallUserAction *action = [[VoIPCallUserAction alloc] initWithAction:ActionCall contact:_contact completion:nil];
[[VoIPCallStateManager shared] processUserAction:action];
} else {
// Alert no internet connection
NSString *title = NSLocalizedString(@"cannot_connect_title", nil);
NSString *message = NSLocalizedString(@"cannot_connect_message", nil);
[UIAlertTemplate showAlertWithOwner:self title:title message:message actionOk:^(UIAlertAction * _Nonnull okAction) {
[self.extensionContext completeRequestReturningItems:@[] completionHandler:^(BOOL expired) {
}];
}];
}
}
- (NSURL*)makeTelUrlForPhone:(NSString*)phoneNumber {
return [NSURL URLWithString:[NSString stringWithFormat:@"tel:%@", [phoneNumber stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]]];
}
- (void)getPhoneCallNumbers {
if (cnContact != nil) {
_callNumbers = nil;
_callNumbers = [NSMutableArray array];
for (CNLabeledValue *phone in cnContact.phoneNumbers) {
NSString *number = [phone.value stringValue];
if ([_callNumbers containsObject:number])
continue;
[_callNumbers addObject:number];
}
}
}
- (BOOL)showPhoneCallButton {
if (cnContact != nil) {
if (_callNumbers.count == 0) {
return NO;
}
return YES;
}
return NO;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (!_contact.isGatewayId && !_contact.isEchoEcho && [UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts) {
return 4;
}
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return _numberOfRowsInSection0;
} else if (section == 1) {
if (_hideActionButtons) {
return 0;
}
NSInteger numRows = [UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1 ? 2 : 1;
if ([ScanIdentityController canScan])
numRows++;
if (canExportConversation)
numRows++;
if (!_contact.isGatewayId && !_contact.isEchoEcho && ([UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureAll || ([UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts && [[UserSettings sharedUserSettings].profilePictureContactList containsObject:_contact.identity])))
numRows++;
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton])
numRows++;
return numRows;
} else if (section == 2) {
if (!_contact.isGatewayId && !_contact.isEchoEcho && [UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts) {
return 1;
} else {
return 2;
}
} else {
return 2;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case 0:
if (indexPath.row == _indexId) {
UITableViewCell *identityCell = [tableView dequeueReusableCellWithIdentifier:@"IdentityCell"];
((UILabel*)[identityCell viewWithTag:100]).text = self.contact.identity;
((UILabel*)[identityCell viewWithTag:100]).isAccessibilityElement = false;
return identityCell;
} else if (indexPath.row == _indexFingerprint) {
KeyFingerprintCell *kfc = [tableView dequeueReusableCellWithIdentifier:@"KeyFingerprintCell"];
kfc.fingerprintLabel.text = [CryptoUtils fingerprintForPublicKey:self.contact.publicKey];
return kfc;
} else if (indexPath.row == _indexVerificationLevel) {
VerificationLevelCell *vlc = [tableView dequeueReusableCellWithIdentifier:@"VerificationLevelCell"];
vlc.contact = self.contact;
vlc.accessibilityTraits = UIAccessibilityTraitButton;
return vlc;
} else if (indexPath.row == _indexNickname) {
UITableViewCell *publicNicknameCell = [tableView dequeueReusableCellWithIdentifier:@"PublicNicknameCell"];
((UILabel*)[publicNicknameCell viewWithTag:101]).text = self.contact.publicNickname;
return publicNicknameCell;
} else if (indexPath.row == _indexGroupMembership) {
UITableViewCell *groupMembershipCell = [tableView dequeueReusableCellWithIdentifier:@"GroupMembershipCell"];
((UILabel*)[groupMembershipCell viewWithTag:102]).text = [NSString stringWithFormat:@"%lu", (unsigned long)[self.contact.groupConversations count]];
groupMembershipCell.accessibilityTraits = UIAccessibilityTraitButton;
return groupMembershipCell;
} else if (indexPath.row == _indexLinkedContact) {
LinkedContactCell *lcc = [tableView dequeueReusableCellWithIdentifier:@"LinkedContactCell"];
lcc.accessibilityTraits = UIAccessibilityTraitButton;
if (cnContact != nil) {
lcc.displayNameLabel.text = [CNContactFormatter stringFromContact:cnContact style:CNContactFormatterStyleFullName];
if (lcc.displayNameLabel.text.length == 0) {
if (cnContact.emailAddresses) {
if (cnContact.emailAddresses.count > 0) {
lcc.displayNameLabel.text = ((CNLabeledValue *)cnContact.emailAddresses.firstObject).value;
}
}
}
} else {
lcc.displayNameLabel.text = [BundleUtil localizedStringForKey:@"(none)"];
}
return lcc;
}
break;
case 1: {
NSString *cellIdentifier;
if (indexPath.row == 0) {
cellIdentifier = @"SendMessageCell";
}
else if (indexPath.row == 1) {
if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
cellIdentifier = @"ThreemaCallCell";
} else {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
cellIdentifier = @"PhoneCallCell";
}
else if (canExportConversation) {
cellIdentifier = @"ExportConversationCell";
}
else if ([ScanIdentityController canScan]) {
cellIdentifier = @"ScanIDCell";
}
else {
cellIdentifier = @"SendProfilePictureCell";
}
}
}
else if (indexPath.row == 2) {
if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
cellIdentifier = @"PhoneCallCell";
}
else if (canExportConversation) {
cellIdentifier = @"ExportConversationCell";
}
else if ([ScanIdentityController canScan]) {
cellIdentifier = @"ScanIDCell";
}
else {
cellIdentifier = @"SendProfilePictureCell";
}
} else {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
if (canExportConversation) {
cellIdentifier = @"ExportConversationCell";
}
else if (canExportConversation && [ScanIdentityController canScan]) {
cellIdentifier = @"ScanIDCell";
}
else {
cellIdentifier = @"SendProfilePictureCell";
}
} else {
if (canExportConversation && [ScanIdentityController canScan]) {
cellIdentifier = @"ScanIDCell";
} else {
cellIdentifier = @"SendProfilePictureCell";
}
}
}
} else if (indexPath.row == 3) {
if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
if (canExportConversation) {
cellIdentifier = @"ExportConversationCell";
}
else if ([ScanIdentityController canScan]) {
cellIdentifier = @"ScanIDCell";
}
else {
cellIdentifier = @"SendProfilePictureCell";
}
} else {
if (canExportConversation && [ScanIdentityController canScan]) {
cellIdentifier = @"ScanIDCell";
} else {
cellIdentifier = @"SendProfilePictureCell";
}
}
} else {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
if (canExportConversation && [ScanIdentityController canScan]) {
cellIdentifier = @"ScanIDCell";
}
else {
cellIdentifier = @"SendProfilePictureCell";
}
} else {
cellIdentifier = @"SendProfilePictureCell";
}
}
}
else if (indexPath.row == 4) {
if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
if (canExportConversation && [ScanIdentityController canScan]) {
cellIdentifier = @"ScanIDCell";
} else {
cellIdentifier = @"SendProfilePictureCell";
}
} else {
cellIdentifier = @"SendProfilePictureCell";
}
} else {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
cellIdentifier = @"SendProfilePictureCell";
}
}
} else if (indexPath.row == 5) {
cellIdentifier = @"SendProfilePictureCell";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell.accessibilityTraits = UIAccessibilityTraitButton;
return cell;
}
case 2: {
if (!_contact.isGatewayId && !_contact.isEchoEcho && [UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts) {
ProfilePictureRecipientCell *profilePictureRecipientCell = [tableView dequeueReusableCellWithIdentifier:@"ProfilePictureRecipientCell"];
profilePictureRecipientCell.identity = self.contact.identity;
profilePictureRecipientCell.delegate = self;
return profilePictureRecipientCell;
} else {
switch (indexPath.row) {
case 0: {
UITableViewCell *pushSettingCell = [tableView dequeueReusableCellWithIdentifier:@"PushSettingCell"];
pushSettingCell.textLabel.text = NSLocalizedString(@"pushSetting_title", @"");
return pushSettingCell;
}
case 1: {
BlockContactCell *bcc = [tableView dequeueReusableCellWithIdentifier:@"BlockCell"];
bcc.identity = self.contact.identity;
return bcc;
}
}
}
break;
}
case 3: {
switch (indexPath.row) {
case 0: {
UITableViewCell *pushSettingCell = [tableView dequeueReusableCellWithIdentifier:@"PushSettingCell"];
pushSettingCell.textLabel.text = NSLocalizedString(@"pushSetting_title", @"");
return pushSettingCell;
}
case 1: {
BlockContactCell *bcc = [tableView dequeueReusableCellWithIdentifier:@"BlockCell"];
bcc.identity = self.contact.identity;
return bcc;
}
}
break;
}
}
return nil;
}
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
if (section == 2 && !_contact.isGatewayId && !_contact.isEchoEcho && [UserSettings sharedUserSettings].sendProfilePicture == SendProfilePictureContacts) {
if ([[UserSettings sharedUserSettings].profilePictureContactList containsObject:_contact.identity]) {
return NSLocalizedString(@"contact_added_to_profilepicture_list", nil);
} else {
return NSLocalizedString(@"contact_removed_from_profilepicture_list", nil);
}
}
return nil;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
[Colors updateTableViewCellBackground:cell];
// handle custom table cells
[Colors setTextColor:[Colors fontNormal] inView:cell.contentView];
if (indexPath.row == _indexId) {
((UILabel*)[cell viewWithTag:100]).textColor = [Colors fontLight];
} else if (indexPath.row == _indexFingerprint) {
KeyFingerprintCell *kfc = (KeyFingerprintCell *)cell;
kfc.fingerprintLabel.textColor = [Colors fontLight];
} else if (indexPath.row == _indexNickname) {
((UILabel*)[cell viewWithTag:101]).textColor = [Colors fontLight];
} else if (indexPath.row == _indexGroupMembership) {
((UILabel*)[cell viewWithTag:102]).textColor = [Colors fontLight];
} else if (indexPath.row == _indexLinkedContact) {
LinkedContactCell *lcc = (LinkedContactCell *)cell;
lcc.displayNameLabel.textColor = [Colors fontLight];
}
} else {
[Colors updateTableViewCell:cell];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case 0:
if (indexPath.row == _indexLinkedContact) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self linkNewContact: cell];
} else if (indexPath.row == _indexGroupMembership) {
ContactGroupMembershipViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"contactGroupMembershipViewController"];
vc.groupContact = self.contact;
[self.navigationController pushViewController:vc animated:YES];
}
break;
case 1:
switch (indexPath.row) {
case 0:
[self sendMessageAction:self];
break;
case 1:
if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
[self startThreemaCallAction];
} else {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
[self startPhoneCallAction];
}
else if (canExportConversation) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self conversationAction:cell];
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}
else if ([ScanIdentityController canScan]) {
[self scanIdentityAction:nil];
}
else {
[self startPofilePictureAction];
}
}
break;
case 2:
if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
[self startPhoneCallAction];
}
else if (canExportConversation) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self conversationAction:cell];
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}
else if ([ScanIdentityController canScan]) {
[self scanIdentityAction:nil];
}
else {
[self startPofilePictureAction];
}
} else {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
if (canExportConversation) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self conversationAction:cell];
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}
else if (canExportConversation && [ScanIdentityController canScan]) {
[self scanIdentityAction:nil];
}
else {
[self startPofilePictureAction];
}
}
else {
if (canExportConversation && [ScanIdentityController canScan]) {
[self scanIdentityAction:nil];
}
else {
[self startPofilePictureAction];
}
}
}
break;
case 3: {
if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
if (canExportConversation) {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[self conversationAction:cell];
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}
else if ([ScanIdentityController canScan]) {
[self scanIdentityAction:nil];
}
else {
[self startPofilePictureAction];
}
} else {
if (canExportConversation && [ScanIdentityController canScan]) {
[self scanIdentityAction:nil];
}
else {
[self startPofilePictureAction];
}
}
} else {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
if (canExportConversation && [ScanIdentityController canScan]) {
[self scanIdentityAction:nil];
}
else {
[self startPofilePictureAction];
}
} else {
[self startPofilePictureAction];
}
}
break;
}
case 4: {
if ([UserSettings sharedUserSettings].enableThreemaCall && is64Bit == 1) {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
if (canExportConversation && [ScanIdentityController canScan]) {
[self scanIdentityAction:nil];
} else {
[self startPofilePictureAction];
}
} else {
[self startPofilePictureAction];
}
} else {
if (SYSTEM_IS_IPAD == NO && [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:+11111"]] && [self showPhoneCallButton]) {
[self startPofilePictureAction];
}
}
break;
}
case 5: {
[self startPofilePictureAction];
break;
}
}
break;
}
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case 0:
if (indexPath.row == _indexVerificationLevel) {
[self performSegueWithIdentifier:@"VerificationSegue" sender:nil];
}
break;
default:
break;
}
}
- (void)showEditContactVC {
EditContactViewController *editVc = [self.storyboard instantiateViewControllerWithIdentifier:@"EditContactViewController"];
editVc.contact = self.contact;
[self.navigationController pushViewController:editVc animated:YES];
}
- (void)linkNewContact:(UIView *)view {
/* Already linked? If so, give user a choice to unlink */
if (cnContact != nil) {
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"unlink_contact", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction * action) {
/* unlink contact */
[[ContactStore sharedContactStore] unlinkContact:self.contact];
[self updateView];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"choose_new_contact", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
[self linkNewContactCheckAuthorization];
}]];
[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}]];
actionSheet.popoverPresentationController.sourceRect = view.frame;
actionSheet.popoverPresentationController.sourceView = self.view;
[self presentViewController:actionSheet animated:YES completion:nil];
} else {
[self linkNewContactCheckAuthorization];
}
}
- (void)linkNewContactCheckAuthorization {
if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] != CNAuthorizationStatusAuthorized) {
[cnAddressBook requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted == YES) {
DDLogInfo(@"Address book access has been granted");
dispatch_async(dispatch_get_main_queue(), ^{
[self linkNewContactPick];
});
} else {
DDLogInfo(@"Address book access has NOT been granted: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *accessAlert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"no_contacts_permission_title", nil) message:NSLocalizedString(@"no_contacts_permission_message", nil) preferredStyle:UIAlertControllerStyleAlert];
if (self.contact.cnContactId != nil) {
[accessAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"unlink_contact", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
[[ContactStore sharedContactStore] unlinkContact:self.contact];
[self updateView];
}]];
}
[accessAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ok", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
}]];
[self presentViewController:accessAlert animated:YES completion:nil];
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
});
}
}];
return;
} else {
[self linkNewContactPick];
}
}
- (void)linkNewContactPick {
CNContactPickerViewController *picker = [[CNContactPickerViewController alloc] init];
picker.delegate = self;
picker.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:picker animated:YES completion:nil];
}
#pragma mark - People picker delegate
-(void) contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{
[(StatusNavigationBar *)self.navigationController.navigationBar showOrHideStatusView];
[[ContactStore sharedContactStore] linkContact:self.contact toCnContactId:contact.identifier];
[self dismissViewControllerAnimated:YES completion:nil];
[self updateView];
}
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty {
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)contactPickerDidCancel:(CNContactPickerViewController *)picker {
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
[(StatusNavigationBar *)self.navigationController.navigationBar showOrHideStatusView];
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - ProfilePictureRecipientCell delegate
- (void)valueChanged:(ProfilePictureRecipientCell *)cell {
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)];
[self.tableView beginUpdates];
[self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
# pragma mark - preview actions
- (NSArray> *)previewActionItems {
NSString *sendMessageTitle = NSLocalizedString(@"send_message", nil);
UIPreviewAction *sendMessageAction = [UIPreviewAction actionWithTitle:sendMessageTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
[self sendMessageAction:self];
}];
NSString *scanQrCodeTitle = NSLocalizedString(@"scan_qr", nil);
UIPreviewAction *scanQrCodeAction = [UIPreviewAction actionWithTitle:scanQrCodeTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
// we need to present contact details first and present qr scanner on top of that
[_delegate presentContactDetails:self onCompletion:^(ContactDetailsViewController *contactsDetailsViewController) {
[contactsDetailsViewController scanIdentityAction:nil];
}];
}];
return @[sendMessageAction, scanQrCodeAction];
}
#pragma mark - Notifications
- (void)colorThemeChanged:(NSNotification*)notification {
[self setupColors];
}
- (void)showProfilePictureChanged:(NSNotification*)notification {
[self updateView];
}
@end