// // Contact.m // linphone // // Created by Gautier Pelloux-Prayer on 12/01/16. // // #import "Contact.h" #import "ContactsListView.h" @implementation Contact - (instancetype)initWithCNContact:(CNContact *)acncontact { return [self initWithPerson:acncontact andFriend:NULL]; } - (instancetype)initWithFriend:(LinphoneFriend *)afriend { return [self initWithPerson:NULL andFriend:afriend]; } - (instancetype)initWithPerson:(CNContact *)acncontact andFriend:(LinphoneFriend *)afriend { self = [super init]; _person = acncontact; _friend = afriend ? linphone_friend_ref(afriend) : NULL; _added = FALSE; _phones = [[NSMutableArray alloc] init]; _sipAddresses = [[NSMutableArray alloc] init]; _emails = [[NSMutableArray alloc] init]; if (_person) { _identifier = _person.identifier; _firstName = _person.givenName; _lastName = _person.familyName; _displayName = [NSString stringWithFormat:@"%@ %@", _firstName, _lastName]; for (CNLabeledValue *phoneNumber in _person.phoneNumbers) { [_phones addObject:phoneNumber.value.stringValue]; } if ([_person respondsToSelector:NSSelectorFromString( CNInstantMessageAddressUsernameKey)] || [_person respondsToSelector:NSSelectorFromString(CNContactInstantMessageAddressesKey)]) { if (_person.instantMessageAddresses != NULL) { for (CNLabeledValue *sipAddr in _person.instantMessageAddresses) { [_sipAddresses addObject:sipAddr.value.username]; } } } for (CNLabeledValue *email in _person.emailAddresses) { [_emails addObject:email.value]; } const char *key = [NSString stringWithFormat:@"ab%@", acncontact.identifier].UTF8String; // try to find friend associated with that person _friend = linphone_friend_list_find_friend_by_ref_key(linphone_core_get_default_friend_list(LC), key); if (!_friend) { _friend = linphone_friend_ref(linphone_core_create_friend(LC)); linphone_friend_set_ref_key(_friend, key); linphone_friend_set_name(_friend, [NSString stringWithFormat:@"%@%@", _firstName ? _firstName : @"", _lastName ? [_firstName ? @" " : @"" stringByAppendingString:_lastName] : @""] .UTF8String); for (NSString *sipAddr in _sipAddresses) { LinphoneAddress *addr = linphone_core_interpret_url(LC, sipAddr.UTF8String); if (addr) { linphone_address_set_display_name(addr, [self displayName].UTF8String); linphone_friend_add_address(_friend, addr); linphone_address_destroy(addr); } } for (NSString *phone in _phones) { linphone_friend_add_phone_number(_friend, phone.UTF8String); } if (_friend) { linphone_friend_enable_subscribes(_friend, FALSE); linphone_friend_set_inc_subscribe_policy(_friend, LinphoneSPDeny); linphone_core_add_friend(LC, _friend); } } linphone_friend_ref(_friend); } else if (_friend) { [self loadFriend]; } else { LOGE(@"Contact cannot be initialized"); return nil; } /* LOGI(@"Contact %@ %@ initialized with %d phones, %d sip, %d emails", self.firstName ?: @"", self.lastName ?: @"", self.phones.count, self.sipAddresses.count, self.emails.count); */ return self; } - (void)dealloc { if (_friend) { linphone_friend_unref(_friend); } _person = nil; _friend = NULL; } #pragma mark - Getters - (UIImage *)avatar { if (_person) { @try { return [UIImage imageWithData:_person.imageData]; } @catch (NSException *e) { LOGE(@"CNContact imageData CNPropertyNotFetchedException : %@", e); return nil; } } return nil; } - (NSString *)displayName { if (_friend) { const char *friend_name = linphone_friend_get_name(_friend); if (friend_name) { return [NSString stringWithUTF8String:friend_name]; } } if (_person != nil) { NSString *lFirstName = _person.givenName; NSString *lLocalizedFirstName = [FastAddressBook localizedLabel:lFirstName]; NSString *compositeName = _person.nickname; NSString *lLastName = _person.familyName; NSString *lLocalizedLastName = [FastAddressBook localizedLabel:lLastName]; NSString *lOrganization = _person.organizationName; NSString *lLocalizedOrganization = [FastAddressBook localizedLabel:lOrganization]; if (compositeName) { return compositeName; } else if (lLocalizedFirstName || lLocalizedLastName) { return [NSString stringWithFormat:@"%@ %@", lLocalizedFirstName, lLocalizedLastName]; } else { return (NSString *)lLocalizedOrganization; } } if (_lastName || _firstName) { NSMutableString *str; if (_firstName) [str appendString:_firstName]; if (_firstName && _lastName) [str appendString:@" "]; if (_lastName) [str appendString:_lastName]; } return NSLocalizedString(@"Unknown", nil); } #pragma mark - Setters - (void)setAvatar:(UIImage *)avatar { BOOL ret = FALSE; if (_person) { NSData *imageAvatar = UIImageJPEGRepresentation(avatar, 0.9f); [_person setValue:imageAvatar forKey:CNContactImageDataKey]; ret = TRUE; } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); } } - (void)setFirstName:(NSString *)firstName { BOOL ret = FALSE; if (![firstName isEqualToString:_firstName]) { if (_friend) ret = linphone_friend_set_name( _friend, [NSString stringWithFormat:@"%@ %@", firstName, _person.familyName] .UTF8String); if (_person) { [_person setValue:firstName forKey:CNContactGivenNameKey]; [_person setValue:[NSString stringWithFormat:@"%@ %@", firstName, _person.familyName] forKey:CNContactNicknameKey]; ret = TRUE; } if (ret) { _firstName = firstName; _displayName = [NSString stringWithFormat:@"%@ %@", firstName, _person.familyName]; } } } - (void)setLastName:(NSString *)lastName { BOOL ret = FALSE; if (_friend) ret = linphone_friend_set_name( _friend, [NSString stringWithFormat:@"%@ %@", _person.givenName, lastName] .UTF8String); if (_person) { [_person setValue:lastName forKey:CNContactFamilyNameKey]; [_person setValue:[NSString stringWithFormat:@"%@ %@", _person.givenName, lastName] forKey:CNContactNicknameKey]; ret = TRUE; } if (ret) { _lastName = lastName; _displayName = [NSString stringWithFormat:@"%@ %@", _person.givenName, lastName]; } } - (BOOL)setSipAddress:(NSString *)sip atIndex:(NSInteger)index { BOOL ret = FALSE; NSString *normSip = NULL; if (_person && ![sip isEqualToString:@" "]) { if ((index + 1) > [_person.instantMessageAddresses count]) { if ([sip hasPrefix:@" "]) normSip = [sip substringFromIndex:1]; else normSip = sip; CNInstantMessageAddress *cNSipMsgAddr; if ([normSip containsString:@"@"]) cNSipMsgAddr = [[CNInstantMessageAddress alloc] initWithUsername:normSip service:[normSip componentsSeparatedByString:@"@"][1]]; else cNSipMsgAddr = [[CNInstantMessageAddress alloc] initWithUsername:normSip service:normSip]; CNLabeledValue *sipAddress = [CNLabeledValue labeledValueWithLabel:NULL value:cNSipMsgAddr]; NSMutableArray *> *tmpSipAddress = [_person.instantMessageAddresses mutableCopy]; [tmpSipAddress addObject:sipAddress]; [_person setValue:tmpSipAddress forKey:CNContactInstantMessageAddressesKey]; ret = TRUE; _sipAddresses[index] = normSip; } else { normSip = sip; CNInstantMessageAddress *cNSipMsgAddr; if ([[FastAddressBook normalizeSipURI:normSip] containsString:@"@"]) cNSipMsgAddr = [[CNInstantMessageAddress alloc] initWithUsername:sip service:[[FastAddressBook normalizeSipURI:normSip] componentsSeparatedByString:@"@"][1]]; else cNSipMsgAddr = [[CNInstantMessageAddress alloc] initWithUsername:normSip service:normSip]; CNLabeledValue *sipAddress = [CNLabeledValue labeledValueWithLabel:NULL value:cNSipMsgAddr]; NSMutableArray *> *tmpSipAddress = [_person.instantMessageAddresses mutableCopy]; [tmpSipAddress replaceObjectAtIndex:index withObject:sipAddress]; [_person setValue:tmpSipAddress forKey:CNContactInstantMessageAddressesKey]; ret = TRUE; } } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); } if (ret) { _sipAddresses[index] = sip; } return ret; } - (BOOL)setPhoneNumber:(NSString *)phone atIndex:(NSInteger)index { BOOL ret = FALSE; if (_person) { if ((index + 1) > [_person.phoneNumbers count]) { CNLabeledValue *mobileNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:[CNPhoneNumber phoneNumberWithStringValue:phone]]; NSMutableArray *> *tmpPhoneNumbers = [_person.phoneNumbers mutableCopy]; [tmpPhoneNumbers addObject:mobileNumber]; [_person setValue:tmpPhoneNumbers forKey:CNContactPhoneNumbersKey]; ret = TRUE; } else { CNLabeledValue *mobileNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:[CNPhoneNumber phoneNumberWithStringValue:phone]]; NSMutableArray *> *tmpPhoneNumbers = [_person.phoneNumbers mutableCopy]; [tmpPhoneNumbers replaceObjectAtIndex:index withObject:mobileNumber]; [_person setValue:tmpPhoneNumbers forKey:CNContactPhoneNumbersKey]; ret = TRUE; } } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); } if (ret) _phones[index] = phone; return ret; } - (BOOL)setEmail:(NSString *)email atIndex:(NSInteger)index { BOOL ret = FALSE; if (_person) { if ((index + 1) > [_person.emailAddresses count]) { CNLabeledValue *emailAddress = [CNLabeledValue labeledValueWithLabel:NULL value:email]; NSMutableArray *> *tmpEmailAddress = [_person.emailAddresses mutableCopy]; [tmpEmailAddress addObject:emailAddress]; [_person setValue:tmpEmailAddress forKey:CNContactEmailAddressesKey]; ret = TRUE; } else { CNLabeledValue *emailAddress = [CNLabeledValue labeledValueWithLabel:NULL value:email]; NSMutableArray *> *tmpEmailAddress = [_person.emailAddresses mutableCopy]; [tmpEmailAddress replaceObjectAtIndex:index withObject:emailAddress]; [_person setValue:tmpEmailAddress forKey:CNContactEmailAddressesKey]; ret = TRUE; } } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); } if (ret) _emails[index] = email; return ret; } - (BOOL)addSipAddress:(NSString *)sip { BOOL ret = FALSE; NSString *normSip = NULL; if (sip != NULL && ![sip isEqualToString:@""]) { if ([sip isEqualToString:@" "]) ret = TRUE; else { if (_person) { normSip = sip; CNInstantMessageAddress *cNSipMsgAddr; if ([normSip containsString:@"@"]) cNSipMsgAddr = [[CNInstantMessageAddress alloc] initWithUsername:[normSip componentsSeparatedByString:@"@"][0] service:[normSip componentsSeparatedByString:@"@"][1]]; else cNSipMsgAddr = [[CNInstantMessageAddress alloc] initWithUsername:normSip service:normSip]; CNLabeledValue *sipAddress = [CNLabeledValue labeledValueWithLabel:NULL value:cNSipMsgAddr]; NSMutableArray *> * tmpSipAddress = [_person.instantMessageAddresses mutableCopy]; [tmpSipAddress addObject:sipAddress]; [_person setValue:tmpSipAddress forKey:CNContactInstantMessageAddressesKey]; ret = TRUE; } else { LinphoneAddress *addr = linphone_core_interpret_url(LC, sip.UTF8String) ?: linphone_address_new(sip.UTF8String); if (addr) { ret = TRUE; linphone_friend_add_address(_friend, addr); linphone_address_destroy(addr); // ensure that it was added by checking list size ret = (bctbx_list_size(linphone_friend_get_addresses(_friend)) == _sipAddresses.count + 1); } } } } if (ret) { if ([sip hasPrefix:@" "]) [_sipAddresses addObject:[sip substringFromIndex:1]]; else [_sipAddresses addObject:sip]; } return ret; } - (BOOL)addPhoneNumber:(NSString *)phone { BOOL ret = FALSE; if (phone != NULL && ![phone isEqualToString:@""]) { if ([phone isEqualToString:@" "]) ret = TRUE; else { if (_person) { CNLabeledValue *mobileNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:[CNPhoneNumber phoneNumberWithStringValue:phone]]; NSMutableArray *> *tmpPhoneNumbers = [_person.phoneNumbers mutableCopy]; [tmpPhoneNumbers addObject:mobileNumber]; [_person setValue:tmpPhoneNumbers forKey:CNContactPhoneNumbersKey]; ret = TRUE; } else { char *cphone = ms_strdup(phone.UTF8String); if (cphone) { linphone_friend_add_phone_number(_friend, cphone); phone = [NSString stringWithUTF8String:cphone]; ms_free(cphone); // ensure that it was added by checking list size ret = (bctbx_list_size(linphone_friend_get_phone_numbers( _friend)) == _phones.count + 1); } } } } if (ret) { [_phones addObject:phone]; } return ret; } - (BOOL)addEmail:(NSString *)email { BOOL ret = FALSE; if (email != NULL && ![email isEqualToString:@""]) { if ([email isEqualToString:@" "]) ret = TRUE; else { if (_person) { CNLabeledValue *emailAddress = [CNLabeledValue labeledValueWithLabel:NULL value:email]; NSMutableArray *> *tmpEmailAddress = [_person.emailAddresses mutableCopy]; [tmpEmailAddress addObject:emailAddress]; [_person setValue:tmpEmailAddress forKey:CNContactEmailAddressesKey]; ret = TRUE; } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); } } } if (ret) { [_emails addObject:email]; } return ret; } - (BOOL)removeSipAddressAtIndex:(NSInteger)index { BOOL ret = FALSE; if (_person ) { NSMutableArray *> *tmpSipAddress = [_person.instantMessageAddresses mutableCopy]; if([tmpSipAddress count] > index){ [tmpSipAddress removeObjectAtIndex:index]; [_person setValue:tmpSipAddress forKey:CNContactInstantMessageAddressesKey]; } ret = TRUE; } else { LinphoneAddress *addr = linphone_core_interpret_url( LC, ((NSString *)_sipAddresses[index]).UTF8String); if (addr) { linphone_friend_remove_address(_friend, addr); linphone_address_destroy(addr); // ensure that it was destroyed by checking list size ret = (bctbx_list_size(linphone_friend_get_addresses(_friend)) + 1 == _sipAddresses.count); } } if (ret) { [_sipAddresses removeObjectAtIndex:index]; } return ret; } - (BOOL)removePhoneNumberAtIndex:(NSInteger)index { BOOL ret = FALSE; if (_person && _person.phoneNumbers.count > 0) { NSMutableArray *> *tmpPhoneNumbers = [_person.phoneNumbers mutableCopy]; if([tmpPhoneNumbers count] > index){ [tmpPhoneNumbers removeObjectAtIndex:index]; [_person setValue:tmpPhoneNumbers forKey:CNContactPhoneNumbersKey]; } ret = TRUE; } else { const char *phone = ((NSString *)_phones[index]).UTF8String; linphone_friend_remove_phone_number(_friend, phone); // ensure that it was destroyed by checking list size // ret = (bctbx_list_size(linphone_friend_get_phone_numbers(_friend)) // + 1 == _phoneNumbers.count); ret = TRUE; } if (ret) { [_phones removeObjectAtIndex:index]; } return ret; } - (BOOL)removeEmailAtIndex:(NSInteger)index { BOOL ret = FALSE; if (_person && _person.phoneNumbers.count > 0) { NSMutableArray *> *tmpEmailAddresses = [_person.emailAddresses mutableCopy]; if([tmpEmailAddresses count] > index){ [tmpEmailAddresses removeObjectAtIndex:index]; [_person setValue:tmpEmailAddresses forKey:CNContactEmailAddressesKey]; } ret = TRUE; } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); } if (ret) { [_emails removeObjectAtIndex:index]; } return ret; } #pragma mark - LinphoneFriend utils - (void)loadFriend { // First and Last name { _firstName = [NSString stringWithUTF8String:linphone_friend_get_name(_friend) ?: ""]; _lastName = nil; } // Phone numbers { _phones = [[NSMutableArray alloc] init]; MSList *numbers = linphone_friend_get_phone_numbers(_friend); while (numbers) { NSString *phone = [NSString stringWithUTF8String:numbers->data]; [_phones addObject:phone]; numbers = numbers->next; } } // SIP (IM) { _sipAddresses = [[NSMutableArray alloc] init]; const MSList *sips = linphone_friend_get_addresses(_friend); while (sips) { LinphoneAddress *addr = sips->data; char *uri = linphone_address_as_string_uri_only(addr); NSString *sipaddr = [NSString stringWithUTF8String:uri]; [_sipAddresses addObject:sipaddr]; ms_free(uri); sips = sips->next; } } // Email - no support for LinphoneFriend { _emails = [[NSMutableArray alloc] init]; } } @end