From e5f2d89847de69b1e34ff539e578a43ff3962e52 Mon Sep 17 00:00:00 2001 From: Gautier Pelloux-Prayer Date: Wed, 18 May 2016 11:55:05 +0200 Subject: [PATCH] Contact: factorize code to handle either native contacts and/or LinphoneFriends --- Classes/ChatConversationCreateTableView.m | 3 +- Classes/Contact.h | 11 +- Classes/Contact.m | 157 ++++++++++----- Classes/ContactDetailsTableView.m | 4 + Classes/ContactDetailsView.h | 9 +- Classes/ContactDetailsView.m | 94 +++------ Classes/ContactsListTableView.h | 3 - Classes/ContactsListTableView.m | 78 ++++---- Classes/HistoryDetailsView.m | 2 +- Classes/LinphoneUI/UIContactCell.h | 2 +- Classes/LinphoneUI/UIContactCell.m | 2 +- Classes/Utils/FastAddressBook.h | 17 +- Classes/Utils/FastAddressBook.m | 222 ++++++++++------------ Classes/Utils/Utils.h | 2 +- Classes/Utils/Utils.m | 4 +- Resources/linphonerc-factory | 3 + linphone_Prefix.pch | 1 + submodules/bctoolbox | 2 +- submodules/cmake-builder | 2 +- submodules/linphone | 2 +- 20 files changed, 311 insertions(+), 309 deletions(-) diff --git a/Classes/ChatConversationCreateTableView.m b/Classes/ChatConversationCreateTableView.m index 293bbf167..06c292608 100644 --- a/Classes/ChatConversationCreateTableView.m +++ b/Classes/ChatConversationCreateTableView.m @@ -35,8 +35,7 @@ [_allContacts enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSString *address = (NSString *)key; - ABRecordRef person = (__bridge ABRecordRef)(value); - NSString *name = [FastAddressBook displayNameForContact:person]; + NSString *name = [FastAddressBook displayNameForContact:value]; if ((filter.length == 0) || ([name.lowercaseString containsSubstring:filter.lowercaseString]) || ([address.lowercaseString containsSubstring:filter.lowercaseString])) { _contacts[address] = name; diff --git a/Classes/Contact.h b/Classes/Contact.h index ec6e1be1d..106ac526d 100644 --- a/Classes/Contact.h +++ b/Classes/Contact.h @@ -13,12 +13,18 @@ @interface Contact : NSObject -@property(nonatomic, assign) NSString *firstName; -@property(nonatomic, assign) NSString *lastName; +@property(nonatomic, readonly) ABRecordRef person; +@property(nonatomic, readonly) LinphoneFriend *friend; + +@property(nonatomic, retain) NSString *firstName; +@property(nonatomic, retain) NSString *lastName; @property(nonatomic, strong) NSMutableArray *sipAddresses; @property(nonatomic, strong) NSMutableArray *emails; @property(nonatomic, strong) NSMutableArray *phoneNumbers; +- (UIImage *)avatar:(BOOL)thumbnail; +- (NSString *)displayName; + - (instancetype)initWithPerson:(ABRecordRef)person; - (instancetype)initWithFriend:(LinphoneFriend *) friend; @@ -33,5 +39,4 @@ - (BOOL)removeSipAddressAtIndex:(NSInteger)index; - (BOOL)removePhoneNumberAtIndex:(NSInteger)index; - (BOOL)removeEmailAtIndex:(NSInteger)index; - @end diff --git a/Classes/Contact.m b/Classes/Contact.m index e984e7559..6ed05fd87 100644 --- a/Classes/Contact.m +++ b/Classes/Contact.m @@ -8,10 +8,7 @@ #import "Contact.h" -@implementation Contact { - ABRecordRef person; - LinphoneFriend *friend; -} +@implementation Contact - (instancetype)initWithPerson:(ABRecordRef)aperson { return [self initWithPerson:aperson andFriend:NULL]; @@ -23,12 +20,12 @@ - (instancetype)initWithPerson:(ABRecordRef)aperson andFriend:(LinphoneFriend *)afriend { self = [super init]; - person = aperson; - friend = afriend; + _person = aperson; + _friend = afriend ? linphone_friend_ref(afriend) : NULL; - if (person) { + if (_person) { [self loadProperties]; - } else if (friend) { + } else if (_friend) { [self loadFriend]; } else { LOGE(@"Contact cannot be initialized"); @@ -41,20 +38,72 @@ } - (void)dealloc { - if (person != nil && ABRecordGetRecordID(person) == kABRecordInvalidID) { - CFRelease(person); + if (_person != nil && ABRecordGetRecordID(_person) == kABRecordInvalidID) { + CFRelease(_person); } - person = nil; - friend = NULL; + if (_friend) { + linphone_friend_unref(_friend); + } + _person = nil; + _friend = NULL; +} + +#pragma mark - Getters +- (UIImage *)avatar:(BOOL)thumbnail { + if (_person && ABPersonHasImageData(_person)) { + NSData *imgData = CFBridgingRelease(ABPersonCopyImageDataWithFormat( + _person, thumbnail ? kABPersonImageFormatThumbnail : kABPersonImageFormatOriginalSize)); + return [UIImage imageWithData:imgData]; + } + return nil; +} + +- (NSString *)displayName { + if (_person != nil) { + NSString *lFirstName = CFBridgingRelease(ABRecordCopyValue(_person, kABPersonFirstNameProperty)); + NSString *lLocalizedFirstName = [FastAddressBook localizedLabel:lFirstName]; + NSString *compositeName = CFBridgingRelease(ABRecordCopyCompositeName(_person)); + + NSString *lLastName = CFBridgingRelease(ABRecordCopyValue(_person, kABPersonLastNameProperty)); + NSString *lLocalizedLastName = [FastAddressBook localizedLabel:lLastName]; + + NSString *lOrganization = CFBridgingRelease(ABRecordCopyValue(_person, kABPersonOrganizationProperty)); + NSString *lLocalizedOrganization = [FastAddressBook localizedLabel:lOrganization]; + + if (compositeName) { + return compositeName; + } else if (lLocalizedFirstName || lLocalizedLastName) { + return [NSString stringWithFormat:@"%@ %@", lLocalizedFirstName, lLocalizedLastName]; + } else { + return (NSString *)lLocalizedOrganization; + } + } else if (_friend) { + const char *dp = linphone_address_get_display_name(linphone_friend_get_address(_friend)); + if (dp) + return [NSString stringWithUTF8String:dp]; + } + + 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)setFirstName:(NSString *)firstName { BOOL ret = FALSE; - if (person) { + if (_person) { ret = ([self replaceInProperty:kABPersonFirstNameProperty value:(__bridge CFTypeRef)(firstName)]); } else { - ret = (linphone_friend_set_name(friend, firstName.UTF8String) == 0); + ret = (linphone_friend_set_name(_friend, firstName.UTF8String) == 0); } if (ret) { @@ -64,7 +113,7 @@ - (void)setLastName:(NSString *)lastName { BOOL ret = FALSE; - if (person) { + if (_person) { ret = ([self replaceInProperty:kABPersonLastNameProperty value:(__bridge CFTypeRef)(lastName)]); } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); @@ -77,7 +126,7 @@ - (BOOL)setSipAddress:(NSString *)sip atIndex:(NSInteger)index { BOOL ret = FALSE; - if (person) { + if (_person) { NSDictionary *lDict = @{ (NSString *) kABPersonInstantMessageUsernameKey : sip, (NSString *) kABPersonInstantMessageServiceKey : LinphoneManager.instance.contactSipField @@ -96,7 +145,7 @@ - (BOOL)setPhoneNumber:(NSString *)phone atIndex:(NSInteger)index { BOOL ret = FALSE; - if (person) { + if (_person) { ret = [self replaceInProperty:kABPersonPhoneProperty value:(__bridge CFTypeRef)(phone) atIndex:index]; } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); @@ -109,7 +158,7 @@ - (BOOL)setEmail:(NSString *)email atIndex:(NSInteger)index { BOOL ret = FALSE; - if (person) { + if (_person) { ret = [self replaceInProperty:kABPersonEmailProperty value:(__bridge CFTypeRef)(email) atIndex:index]; } else { } @@ -121,7 +170,7 @@ - (BOOL)addSipAddress:(NSString *)sip { BOOL ret = FALSE; - if (person) { + if (_person) { NSDictionary *lDict = @{ (NSString *) kABPersonInstantMessageUsernameKey : sip, (NSString *) kABPersonInstantMessageServiceKey : LinphoneManager.instance.contactSipField @@ -129,13 +178,13 @@ ret = [self addInProperty:kABPersonInstantMessageProperty value:(__bridge CFTypeRef)(lDict)]; } else { - LinphoneAddress *addr = linphone_core_interpret_url(LC, sip.UTF8String); + 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_friend_add_address(_friend, addr); linphone_address_destroy(addr); // ensure that it was added by checking list size - ret = (ms_list_size(linphone_friend_get_addresses(friend)) == _sipAddresses.count + 1); + ret = (ms_list_size(linphone_friend_get_addresses(_friend)) == _sipAddresses.count + 1); } } if (ret) { @@ -146,16 +195,17 @@ - (BOOL)addPhoneNumber:(NSString *)phone { BOOL ret = FALSE; - if (person) { + if (_person) { ret = [self addInProperty:kABPersonPhoneProperty value:(__bridge CFTypeRef)(phone)]; } else { - char *cphone = linphone_proxy_config_normalize_phone_number(NULL, phone.UTF8String); + char *cphone = + linphone_proxy_config_normalize_phone_number(NULL, phone.UTF8String) ?: ms_strdup(phone.UTF8String); if (cphone) { - linphone_friend_add_phone_number(friend, 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 = (ms_list_size(linphone_friend_get_phone_numbers(friend)) == _phoneNumbers.count + 1); + ret = (ms_list_size(linphone_friend_get_phone_numbers(_friend)) == _phoneNumbers.count + 1); } } if (ret) { @@ -166,7 +216,7 @@ - (BOOL)addEmail:(NSString *)email { BOOL ret = FALSE; - if (person) { + if (_person) { ret = [self addInProperty:kABPersonEmailProperty value:(__bridge CFTypeRef)(email)]; } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); @@ -179,15 +229,15 @@ - (BOOL)removeSipAddressAtIndex:(NSInteger)index { BOOL ret = FALSE; - if (person) { + if (_person) { ret = [self removeInProperty:kABPersonInstantMessageProperty atIndex:index]; } else { LinphoneAddress *addr = linphone_core_interpret_url(LC, ((NSString *)_sipAddresses[index]).UTF8String); if (addr) { - linphone_friend_remove_address(friend, addr); + linphone_friend_remove_address(_friend, addr); linphone_address_destroy(addr); // ensure that it was destroyed by checking list size - ret = (ms_list_size(linphone_friend_get_addresses(friend)) == _sipAddresses.count - 1); + ret = (ms_list_size(linphone_friend_get_addresses(_friend)) == _sipAddresses.count - 1); } } if (ret) { @@ -198,13 +248,13 @@ - (BOOL)removePhoneNumberAtIndex:(NSInteger)index { BOOL ret = FALSE; - if (person) { + if (_person) { ret = [self removeInProperty:kABPersonPhoneProperty atIndex:index]; } else { const char *phone = ((NSString *)_phoneNumbers[index]).UTF8String; - linphone_friend_remove_phone_number(friend, phone); + linphone_friend_remove_phone_number(_friend, phone); // ensure that it was destroyed by checking list size - ret = (ms_list_size(linphone_friend_get_phone_numbers(friend)) == _phoneNumbers.count - 1); + ret = (ms_list_size(linphone_friend_get_phone_numbers(_friend)) == _phoneNumbers.count - 1); } if (ret) { [_phoneNumbers removeObjectAtIndex:index]; @@ -214,7 +264,7 @@ - (BOOL)removeEmailAtIndex:(NSInteger)index { BOOL ret = FALSE; - if (person) { + if (_person) { ret = [self removeInProperty:kABPersonEmailProperty atIndex:index]; } else { LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__); @@ -230,23 +280,29 @@ - (void)loadProperties { // First and Last name { - _firstName = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty)); - _lastName = (NSString *)CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty)); + _firstName = (NSString *)CFBridgingRelease(ABRecordCopyValue(_person, kABPersonFirstNameProperty)); + _lastName = (NSString *)CFBridgingRelease(ABRecordCopyValue(_person, kABPersonLastNameProperty)); } // Phone numbers { _phoneNumbers = [[NSMutableArray alloc] init]; - ABMultiValueRef map = ABRecordCopyValue(person, kABPersonPhoneProperty); + ABMultiValueRef map = ABRecordCopyValue(_person, kABPersonPhoneProperty); if (map) { for (int i = 0; i < ABMultiValueGetCount(map); ++i) { ABMultiValueIdentifier identifier = ABMultiValueGetIdentifierAtIndex(map, i); NSInteger index = ABMultiValueGetIndexForIdentifier(map, identifier); if (index != -1) { NSString *valueRef = CFBridgingRelease(ABMultiValueCopyValueAtIndex(map, index)); + char *normalizedPhone = linphone_proxy_config_normalize_phone_number( + linphone_core_get_default_proxy_config(LC), valueRef.UTF8String); + NSString *name = [FastAddressBook + normalizeSipURI:normalizedPhone ? [NSString stringWithUTF8String:normalizedPhone] : valueRef]; if (valueRef != NULL) { - [_phoneNumbers addObject:[FastAddressBook localizedLabel:valueRef]]; + [_phoneNumbers addObject:name ?: [FastAddressBook localizedLabel:valueRef]]; } + if (normalizedPhone) + ms_free(normalizedPhone); } } CFRelease(map); @@ -256,7 +312,7 @@ // SIP (IM) { _sipAddresses = [[NSMutableArray alloc] init]; - ABMultiValueRef map = ABRecordCopyValue(person, kABPersonInstantMessageProperty); + ABMultiValueRef map = ABRecordCopyValue(_person, kABPersonInstantMessageProperty); if (map) { for (int i = 0; i < ABMultiValueGetCount(map); ++i) { CFDictionaryRef lDict = ABMultiValueCopyValueAtIndex(map, i); @@ -267,7 +323,7 @@ NSString *value = (NSString *)(CFDictionaryGetValue(lDict, kABPersonInstantMessageUsernameKey)); CFRelease(lDict); if (value != NULL) { - [_sipAddresses addObject:value]; + [_sipAddresses addObject:[FastAddressBook normalizeSipURI:value] ?: value]; } } } @@ -279,7 +335,7 @@ // Email { _emails = [[NSMutableArray alloc] init]; - ABMultiValueRef map = ABRecordCopyValue(person, kABPersonEmailProperty); + ABMultiValueRef map = ABRecordCopyValue(_person, kABPersonEmailProperty); if (map) { for (int i = 0; i < ABMultiValueGetCount(map); ++i) { ABMultiValueIdentifier identifier = ABMultiValueGetIdentifierAtIndex(map, i); @@ -298,15 +354,15 @@ - (BOOL)replaceInProperty:(ABPropertyID)property value:(CFTypeRef)value { CFErrorRef error = NULL; - if (!ABRecordSetValue(person, property, value, &error)) { - LOGE(@"Error when saving property %d in contact %p: Fail(%@)", property, person, error); + if (!ABRecordSetValue(_person, property, value, &error)) { + LOGE(@"Error when saving property %d in contact %p: Fail(%@)", property, _person, error); return NO; } return YES; } - (BOOL)replaceInProperty:(ABPropertyID)property value:(CFTypeRef)value atIndex:(NSInteger)index { - ABMultiValueRef lcMap = ABRecordCopyValue(person, property); + ABMultiValueRef lcMap = ABRecordCopyValue(_person, property); ABMutableMultiValueRef lMap; if (lcMap != NULL) { lMap = ABMultiValueCreateMutableCopy(lcMap); @@ -327,7 +383,7 @@ } - (BOOL)addInProperty:(ABPropertyID)property value:(CFTypeRef)value { - ABMultiValueRef lcMap = ABRecordCopyValue(person, property); + ABMultiValueRef lcMap = ABRecordCopyValue(_person, property); ABMutableMultiValueRef lMap; if (lcMap != NULL) { lMap = ABMultiValueCreateMutableCopy(lcMap); @@ -349,7 +405,7 @@ } - (BOOL)removeInProperty:(ABPropertyID)property atIndex:(NSInteger)index { - ABMultiValueRef lcMap = ABRecordCopyValue(person, property); + ABMultiValueRef lcMap = ABRecordCopyValue(_person, property); ABMutableMultiValueRef lMap; if (lcMap != NULL) { lMap = ABMultiValueCreateMutableCopy(lcMap); @@ -373,12 +429,15 @@ - (void)loadFriend { // First and Last name - { _firstName = [NSString stringWithUTF8String:linphone_friend_get_name(friend) ?: ""]; } + { + _firstName = [NSString stringWithUTF8String:linphone_friend_get_name(_friend) ?: ""]; + _lastName = nil; + } // Phone numbers { _phoneNumbers = [[NSMutableArray alloc] init]; - MSList *numbers = linphone_friend_get_phone_numbers(friend); + MSList *numbers = linphone_friend_get_phone_numbers(_friend); while (numbers) { NSString *phone = [NSString stringWithUTF8String:numbers->data]; [_phoneNumbers addObject:[FastAddressBook localizedLabel:phone]]; @@ -389,7 +448,7 @@ // SIP (IM) { _sipAddresses = [[NSMutableArray alloc] init]; - MSList *sips = linphone_friend_get_addresses(friend); + MSList *sips = linphone_friend_get_addresses(_friend); while (sips) { LinphoneAddress *addr = sips->data; char *uri = linphone_address_as_string_uri_only(addr); diff --git a/Classes/ContactDetailsTableView.m b/Classes/ContactDetailsTableView.m index f95c893e8..fd0fba1fe 100644 --- a/Classes/ContactDetailsTableView.m +++ b/Classes/ContactDetailsTableView.m @@ -66,6 +66,8 @@ if (rmed) { [tableview deleteRowsAtIndexPaths:@[ path ] withRowAnimation:animated ? UITableViewRowAnimationFade : UITableViewRowAnimationNone]; + } else { + LOGW(@"Cannot remove entry at path %@, skipping", path); } } @@ -83,6 +85,8 @@ NSUInteger count = [self getSectionData:section].count; [tableview insertRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:count - 1 inSection:section] ] withRowAnimation:animated ? UITableViewRowAnimationFade : UITableViewRowAnimationNone]; + } else { + LOGW(@"Cannot add entry '%@' in section %d, skipping", value, section); } } diff --git a/Classes/ContactDetailsView.h b/Classes/ContactDetailsView.h index 1d6d512fd..d134c5cc3 100644 --- a/Classes/ContactDetailsView.h +++ b/Classes/ContactDetailsView.h @@ -26,11 +26,10 @@ #import "ImagePickerView.h" @interface ContactDetailsView : TPMultiLayoutViewController { - ABAddressBookRef addressBook; BOOL inhibUpdate; } -@property(nonatomic, assign, setter=setContact:) ABRecordRef contact; +@property(nonatomic, assign, setter=setContact:) Contact *contact; @property(nonatomic, strong) IBOutlet ContactDetailsTableView *tableController; @property(nonatomic, strong) IBOutlet UIToggleButton *editButton; @property(nonatomic, strong) IBOutlet UIButton *backButton; @@ -49,7 +48,7 @@ - (void)newContact; - (void)newContact:(NSString *)address; -- (void)editContact:(ABRecordRef)contact; -- (void)editContact:(ABRecordRef)contact address:(NSString *)address; -- (void)setContact:(ABRecordRef)contact; +- (void)editContact:(Contact *)contact; +- (void)editContact:(Contact *)contact address:(NSString *)address; +- (void)setContact:(Contact *)contact; @end diff --git a/Classes/ContactDetailsView.m b/Classes/ContactDetailsView.m index 38bd64531..b2623efbe 100644 --- a/Classes/ContactDetailsView.m +++ b/Classes/ContactDetailsView.m @@ -22,46 +22,46 @@ @implementation ContactDetailsView -static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void *context); - #pragma mark - Lifecycle Functions - (id)init { self = [super initWithNibName:NSStringFromClass(self.class) bundle:[NSBundle mainBundle]]; if (self != nil) { inhibUpdate = FALSE; - addressBook = ABAddressBookCreateWithOptions(nil, nil); - ABAddressBookRegisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(self)); + [NSNotificationCenter.defaultCenter addObserver:self + selector:@selector(onAddressBookUpdate:) + name:kLinphoneAddressBookUpdate + object:nil]; } return self; } - (void)dealloc { - ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(self)); - CFRelease(addressBook); + [NSNotificationCenter.defaultCenter removeObserver:self]; } #pragma mark - +- (void)onAddressBookUpdate:(NSNotification *)k { + if (!inhibUpdate && ![_tableController isEditing]) { + [self resetData]; + } +} + - (void)resetData { if (self.isEditing) { [self setEditing:FALSE]; } if (_contact == NULL) { - ABAddressBookRevert(addressBook); return; } - ABRecordID recordID = ABRecordGetRecordID(_contact); - ABAddressBookRevert(addressBook); - _contact = ABAddressBookGetPersonWithRecordID(addressBook, recordID); - if (_contact != NULL) { LOGI(@"Reset data to contact %p", _contact); [_avatarImage setImage:[FastAddressBook imageForContact:_contact thumbnail:NO] bordered:NO withRoundedRadius:YES]; - [_tableController setContact:[[Contact alloc] initWithPerson:_contact]]; + [_tableController setContact:_contact]; _emptyLabel.hidden = YES; } else { _emptyLabel.hidden = NO; @@ -71,13 +71,6 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info } } -static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void *context) { - ContactDetailsView *controller = (__bridge ContactDetailsView *)context; - if (!controller->inhibUpdate && ![[controller tableController] isEditing]) { - [controller resetData]; - } -} - - (void)removeContact { if (_contact != NULL) { inhibUpdate = TRUE; @@ -94,41 +87,20 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info } // Add contact to book - CFErrorRef error = NULL; - if (ABRecordGetRecordID(_contact) == kABRecordInvalidID) { - ABAddressBookAddRecord(addressBook, _contact, (CFErrorRef *)&error); - if (error != NULL) { - LOGE(@"Add contact %p: Fail(%@)", _contact, [(__bridge NSError *)error localizedDescription]); - } else { - LOGI(@"Add contact %p: Success!", _contact); - } - } - - // Save address book - error = NULL; - inhibUpdate = TRUE; - ABAddressBookSave(addressBook, &error); - inhibUpdate = FALSE; - if (error != NULL) { - LOGE(@"Save AddressBook: Fail(%@)", [(__bridge NSError *)error localizedDescription]); - } else { - LOGI(@"Save AddressBook: Success!"); - } - [LinphoneManager.instance.fastAddressBook reload]; + [LinphoneManager.instance.fastAddressBook saveContact:_contact]; } -- (void)selectContact:(ABRecordRef)acontact andReload:(BOOL)reload { +- (void)selectContact:(Contact *)acontact andReload:(BOOL)reload { if (self.isEditing) { [self setEditing:FALSE]; } - ABAddressBookRevert(addressBook); _contact = acontact; _emptyLabel.hidden = (_contact != NULL); [_avatarImage setImage:[FastAddressBook imageForContact:_contact thumbnail:NO] bordered:NO withRoundedRadius:YES]; - [ContactDisplay setDisplayNameLabel:_nameLabel forContact:acontact]; - [_tableController setContact:[[Contact alloc] initWithPerson:_contact]]; + [ContactDisplay setDisplayNameLabel:_nameLabel forContact:_contact]; + [_tableController setContact:_contact]; if (reload) { [self setEditing:TRUE animated:FALSE]; @@ -157,25 +129,25 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info } - (void)newContact { - [self selectContact:ABPersonCreate() andReload:YES]; + [self selectContact:[[Contact alloc] initWithPerson:ABPersonCreate()] andReload:YES]; } - (void)newContact:(NSString *)address { - [self selectContact:ABPersonCreate() andReload:NO]; + [self selectContact:[[Contact alloc] initWithPerson:ABPersonCreate()] andReload:NO]; [self addCurrentContactContactField:address]; } -- (void)editContact:(ABRecordRef)acontact { - [self selectContact:ABAddressBookGetPersonWithRecordID(addressBook, ABRecordGetRecordID(acontact)) andReload:YES]; +- (void)editContact:(Contact *)acontact { + [self selectContact:acontact andReload:YES]; } -- (void)editContact:(ABRecordRef)acontact address:(NSString *)address { - [self selectContact:ABAddressBookGetPersonWithRecordID(addressBook, ABRecordGetRecordID(acontact)) andReload:NO]; +- (void)editContact:(Contact *)acontact address:(NSString *)address { + [self selectContact:acontact andReload:NO]; [self addCurrentContactContactField:address]; } -- (void)setContact:(ABRecordRef)acontact { - [self selectContact:ABAddressBookGetPersonWithRecordID(addressBook, ABRecordGetRecordID(acontact)) andReload:NO]; +- (void)setContact:(Contact *)acontact { + [self selectContact:acontact andReload:NO]; } #pragma mark - ViewController Functions @@ -348,23 +320,7 @@ static UICompositeViewDescription *compositeDescription = nil; [VIEW(ImagePickerView).popoverController dismissPopoverAnimated:TRUE]; } - FastAddressBook *fab = LinphoneManager.instance.fastAddressBook; - CFErrorRef error = NULL; - if (!ABPersonRemoveImageData(_contact, (CFErrorRef *)&error)) { - LOGI(@"Can't remove entry: %@", [(__bridge NSError *)error localizedDescription]); - } - NSData *dataRef = UIImageJPEGRepresentation(image, 0.9f); - CFDataRef cfdata = CFDataCreate(NULL, [dataRef bytes], [dataRef length]); - - [fab saveAddressBook]; - - if (!ABPersonSetImageData(_contact, cfdata, (CFErrorRef *)&error)) { - LOGI(@"Can't add entry: %@", [(__bridge NSError *)error localizedDescription]); - } else { - [fab saveAddressBook]; - } - - CFRelease(cfdata); + [FastAddressBook setAvatar:image forContact:_contact]; [_avatarImage setImage:[FastAddressBook imageForContact:_contact thumbnail:NO] bordered:NO withRoundedRadius:YES]; } diff --git a/Classes/ContactsListTableView.h b/Classes/ContactsListTableView.h index 135479ec2..634a0bf71 100644 --- a/Classes/ContactsListTableView.h +++ b/Classes/ContactsListTableView.h @@ -27,9 +27,6 @@ @interface ContactsListTableView : UICheckBoxTableView { @private OrderedDictionary *addressBookMap; - NSMutableDictionary *avatarMap; - - ABAddressBookRef addressBook; } - (void)loadData; diff --git a/Classes/ContactsListTableView.m b/Classes/ContactsListTableView.m index 1e2f995b5..34c8a414f 100644 --- a/Classes/ContactsListTableView.m +++ b/Classes/ContactsListTableView.m @@ -25,17 +25,19 @@ @implementation ContactsListTableView -static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void *context); - #pragma mark - Lifecycle Functions - (void)initContactsTableViewController { addressBookMap = [[OrderedDictionary alloc] init]; - avatarMap = [[NSMutableDictionary alloc] init]; - addressBook = ABAddressBookCreateWithOptions(nil, nil); + [NSNotificationCenter.defaultCenter addObserver:self + selector:@selector(onAddressBookUpdate:) + name:kLinphoneAddressBookUpdate + object:nil]; +} - ABAddressBookRegisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(self)); +- (void)onAddressBookUpdate:(NSNotification *)k { + [self loadData]; } - (void)viewWillAppear:(BOOL)animated { @@ -65,9 +67,7 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info } - (void)dealloc { - if (addressBook) { - ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(self)); - } + [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - @@ -92,7 +92,7 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) { return (int)(within_sentence != NULL ? 0 : fuzzy_word + strlen(fuzzy_word) - c); } -- (NSString *)displayNameForContact:(ABRecordRef)person { +- (NSString *)displayNameForContact:(Contact *)person { NSString *name = [FastAddressBook displayNameForContact:person]; if (name != nil && [name length] > 0 && ![name isEqualToString:NSLocalizedString(@"Unknown", nil)]) { // Add the contact only if it fuzzy match filter too (if any) @@ -116,27 +116,24 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) { // Reset Address book [addressBookMap removeAllObjects]; - NSArray *lContacts = (NSArray *)CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook)); - for (id lPerson in lContacts) { + for (NSString *addr in LinphoneManager.instance.fastAddressBook.addressBookMap) { + Contact *contact = [LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:addr]; + BOOL add = true; - ABRecordRef person = (__bridge ABRecordRef)lPerson; // Do not add the contact directly if we set some filter if ([ContactSelection getSipFilter] || [ContactSelection emailFilterEnabled]) { add = false; } - if ([FastAddressBook contactHasValidSipDomain:person]) { + if ([FastAddressBook contactHasValidSipDomain:contact]) { add = true; } if (!add && [ContactSelection emailFilterEnabled]) { - ABMultiValueRef personEmailAddresses = ABRecordCopyValue(person, kABPersonEmailProperty); // Add this contact if it has an email - add = (ABMultiValueGetCount(personEmailAddresses) > 0); - - CFRelease(personEmailAddresses); + add = (contact.emails.count > 0); } - NSString *name = [self displayNameForContact:person]; + NSString *name = [self displayNameForContact:contact]; if (add && name != nil) { NSString *firstChar = [[name substringToIndex:1] uppercaseString]; @@ -149,7 +146,7 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) { subDic = [[OrderedDictionary alloc] init]; [addressBookMap insertObject:subDic forKey:firstChar selector:@selector(caseInsensitiveCompare:)]; } - [subDic insertObject:lPerson forKey:name selector:@selector(caseInsensitiveCompare:)]; + [subDic insertObject:contact forKey:name selector:@selector(caseInsensitiveCompare:)]; } } [super loadData]; @@ -166,13 +163,6 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) { } } -static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void *context) { - ContactsListTableView *controller = (__bridge ContactsListTableView *)context; - ABAddressBookRevert(addressBook); - [controller->avatarMap removeAllObjects]; - [controller loadData]; -} - #pragma mark - UITableViewDataSource Functions - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { @@ -200,14 +190,10 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info if (cell == nil) { cell = [[UIContactCell alloc] initWithIdentifier:kCellId]; } - ABRecordRef contact = [self contactForIndexPath:indexPath]; + Contact *contact = [self contactForIndexPath:indexPath]; // Cached avatar - UIImage *image = [avatarMap objectForKey:[NSNumber numberWithInt:ABRecordGetRecordID(contact)]]; - if (image == nil) { - image = [FastAddressBook imageForContact:contact thumbnail:true]; - [avatarMap setObject:image forKey:[NSNumber numberWithInt:ABRecordGetRecordID(contact)]]; - } + UIImage *image = [FastAddressBook imageForContact:contact thumbnail:true]; [cell.avatarImage setImage:image bordered:NO withRoundedRadius:YES]; [cell setContact:contact]; [super accessoryForCell:cell atPath:indexPath]; @@ -236,15 +222,15 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info [super tableView:tableView didSelectRowAtIndexPath:indexPath]; if (![self isEditing]) { OrderedDictionary *subDic = [addressBookMap objectForKey:[addressBookMap keyAtIndex:[indexPath section]]]; - ABRecordRef lPerson = (__bridge ABRecordRef)([subDic objectForKey:[subDic keyAtIndex:[indexPath row]]]); + Contact *contact = [subDic objectForKey:[subDic keyAtIndex:[indexPath row]]]; // Go to Contact details view ContactDetailsView *view = VIEW(ContactDetailsView); [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; if (([ContactSelection getSelectionMode] != ContactSelectionModeEdit) || !([ContactSelection getAddAddress])) { - [view setContact:lPerson]; + [view setContact:contact]; } else { - [view editContact:lPerson address:[ContactSelection getAddAddress]]; + [view editContact:contact address:[ContactSelection getAddAddress]]; } } } @@ -253,39 +239,49 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { - ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(self)); + [NSNotificationCenter.defaultCenter removeObserver:self]; [tableView beginUpdates]; + NSString *firstChar = [addressBookMap keyAtIndex:[indexPath section]]; OrderedDictionary *subDic = [addressBookMap objectForKey:firstChar]; NSString *key = [[subDic allKeys] objectAtIndex:[indexPath row]]; - ABRecordRef contact = (__bridge ABRecordRef)([subDic objectForKey:key]); + Contact *contact = [subDic objectForKey:key]; [[addressBookMap objectForKey:firstChar] removeObjectForKey:[self displayNameForContact:contact]]; if ([tableView numberOfRowsInSection:indexPath.section] == 1) { [addressBookMap removeObjectForKey:firstChar]; [tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade]; } - [[LinphoneManager.instance fastAddressBook] removeContact:contact]; [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView endUpdates]; - ABAddressBookRegisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(self)); + + [NSNotificationCenter.defaultCenter addObserver:self + selector:@selector(onAddressBookUpdate:) + name:kLinphoneAddressBookUpdate + object:nil]; } } - (void)removeSelectionUsing:(void (^)(NSIndexPath *))remover { [super removeSelectionUsing:^(NSIndexPath *indexPath) { - ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(self)); + [NSNotificationCenter.defaultCenter removeObserver:self]; + NSString *firstChar = [addressBookMap keyAtIndex:[indexPath section]]; OrderedDictionary *subDic = [addressBookMap objectForKey:firstChar]; NSString *key = [[subDic allKeys] objectAtIndex:[indexPath row]]; - ABRecordRef contact = (__bridge ABRecordRef)([subDic objectForKey:key]); + Contact *contact = [subDic objectForKey:key]; [[addressBookMap objectForKey:firstChar] removeObjectForKey:[self displayNameForContact:contact]]; if ([self.tableView numberOfRowsInSection:indexPath.section] == 1) { [addressBookMap removeObjectForKey:firstChar]; } [[LinphoneManager.instance fastAddressBook] removeContact:contact]; + + [NSNotificationCenter.defaultCenter addObserver:self + selector:@selector(onAddressBookUpdate:) + name:kLinphoneAddressBookUpdate + object:nil]; }]; } diff --git a/Classes/HistoryDetailsView.m b/Classes/HistoryDetailsView.m index 521b7bceb..e0d946807 100644 --- a/Classes/HistoryDetailsView.m +++ b/Classes/HistoryDetailsView.m @@ -140,7 +140,7 @@ static UICompositeViewDescription *compositeDescription = nil; - (IBAction)onContactClick:(id)event { LinphoneAddress *addr = linphone_call_log_get_remote_address(callLog); - ABRecordRef contact = [FastAddressBook getContactWithAddress:addr]; + Contact *contact = [FastAddressBook getContactWithAddress:addr]; if (contact) { ContactDetailsView *view = VIEW(ContactDetailsView); [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; diff --git a/Classes/LinphoneUI/UIContactCell.h b/Classes/LinphoneUI/UIContactCell.h index 0729a859b..63fc930ee 100644 --- a/Classes/LinphoneUI/UIContactCell.h +++ b/Classes/LinphoneUI/UIContactCell.h @@ -26,7 +26,7 @@ @property(nonatomic, strong) IBOutlet UILabel *nameLabel; @property(nonatomic, strong) IBOutlet UIRoundedImageView *avatarImage; @property(weak, nonatomic) IBOutlet UIImageView *linphoneImage; -@property (nonatomic, assign) ABRecordRef contact; +@property(nonatomic, assign) Contact *contact; - (id)initWithIdentifier:(NSString*)identifier; diff --git a/Classes/LinphoneUI/UIContactCell.m b/Classes/LinphoneUI/UIContactCell.m index 0fdd62a12..c31b9d058 100644 --- a/Classes/LinphoneUI/UIContactCell.m +++ b/Classes/LinphoneUI/UIContactCell.m @@ -49,7 +49,7 @@ #pragma mark - Property Functions -- (void)setContact:(ABRecordRef)acontact { +- (void)setContact:(Contact *)acontact { _contact = acontact; [ContactDisplay setDisplayNameLabel:_nameLabel forContact:_contact]; _linphoneImage.hidden = !([FastAddressBook contactHasValidSipDomain:_contact]); diff --git a/Classes/Utils/FastAddressBook.h b/Classes/Utils/FastAddressBook.h index e7e229abe..059a3f8e3 100644 --- a/Classes/Utils/FastAddressBook.h +++ b/Classes/Utils/FastAddressBook.h @@ -21,34 +21,35 @@ #import #include "linphone/linphonecore.h" +#include "Contact.h" -@interface FastAddressBook : NSObject { - ABAddressBookRef addressBook; -} +@interface FastAddressBook : NSObject @property(readonly, nonatomic) NSMutableDictionary *addressBookMap; - (void)reload; - (void)saveAddressBook; -- (int)removeContact:(ABRecordRef)contact; +- (int)removeContact:(Contact *)contact; +- (BOOL)saveContact:(Contact *)contact; + (BOOL)isAuthorized; // TOOLS -+ (ABRecordRef)getContactWithAddress:(const LinphoneAddress *)address; ++ (Contact *)getContactWithAddress:(const LinphoneAddress *)address; -+ (UIImage *)imageForContact:(ABRecordRef)contact thumbnail:(BOOL)thumbnail; ++ (UIImage *)imageForContact:(Contact *)contact thumbnail:(BOOL)thumbnail; + (UIImage *)imageForAddress:(const LinphoneAddress *)addr thumbnail:(BOOL)thumbnail; -+ (BOOL)contactHasValidSipDomain:(ABRecordRef)person; ++ (BOOL)contactHasValidSipDomain:(Contact *)person; -+ (NSString *)displayNameForContact:(ABRecordRef)person; ++ (NSString *)displayNameForContact:(Contact *)person; + (NSString *)displayNameForAddress:(const LinphoneAddress *)addr; + (BOOL)isSipURI:(NSString *)address; // should be removed + (NSString *)normalizeSipURI:(NSString *)address; // should be removed + (NSString *)localizedLabel:(NSString *)label; ++ (void)setAvatar:(UIImage *)avatar forContact:(Contact *)contact; @end diff --git a/Classes/Utils/FastAddressBook.m b/Classes/Utils/FastAddressBook.m index 5ebc98349..73b6fdeea 100644 --- a/Classes/Utils/FastAddressBook.m +++ b/Classes/Utils/FastAddressBook.m @@ -22,18 +22,14 @@ #import "ContactsListView.h" #import "Utils.h" -@implementation FastAddressBook +@implementation FastAddressBook { + ABAddressBookRef addressBook; +} static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void *context); -+ (UIImage *)imageForContact:(ABRecordRef)contact thumbnail:(BOOL)thumbnail { - UIImage *retImage = nil; - if (contact && ABPersonHasImageData(contact)) { - NSData *imgData = CFBridgingRelease(ABPersonCopyImageDataWithFormat( - contact, thumbnail ? kABPersonImageFormatThumbnail : kABPersonImageFormatOriginalSize)); - - retImage = [UIImage imageWithData:imgData]; - } ++ (UIImage *)imageForContact:(Contact *)contact thumbnail:(BOOL)thumbnail { + UIImage *retImage = [contact avatar:thumbnail]; if (retImage == nil) { retImage = [UIImage imageNamed:@"avatar.png"]; } @@ -50,17 +46,17 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info return [FastAddressBook imageForContact:[FastAddressBook getContactWithAddress:addr] thumbnail:thumbnail]; } -+ (ABRecordRef)getContact:(NSString *)address { ++ (Contact *)getContact:(NSString *)address { if (LinphoneManager.instance.fastAddressBook != nil) { @synchronized(LinphoneManager.instance.fastAddressBook.addressBookMap) { - return (__bridge ABRecordRef)[LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:address]; + return [LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:address]; } } return nil; } -+ (ABRecordRef)getContactWithAddress:(const LinphoneAddress *)address { - ABRecordRef contact = nil; ++ (Contact *)getContactWithAddress:(const LinphoneAddress *)address { + Contact *contact = nil; if (address) { char *uri = linphone_address_as_string_uri_only(address); NSString *normalizedSipAddress = [FastAddressBook normalizeSipURI:[NSString stringWithUTF8String:uri]]; @@ -135,67 +131,43 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info } } +- (void)registerAddrsFor:(Contact *)contact { + for (NSString *phone in contact.phoneNumbers) { + [_addressBookMap setObject:contact forKey:phone]; + } + for (NSString *sip in contact.sipAddresses) { + [_addressBookMap setObject:contact forKey:sip]; + } +} + - (void)loadData { @synchronized(_addressBookMap) { ABAddressBookRevert(addressBook); [_addressBookMap removeAllObjects]; + // load native contacts CFArrayRef lContacts = ABAddressBookCopyArrayOfAllPeople(addressBook); CFIndex count = CFArrayGetCount(lContacts); for (CFIndex idx = 0; idx < count; idx++) { ABRecordRef lPerson = CFArrayGetValueAtIndex(lContacts, idx); - // Phone - { - ABMultiValueRef lMap = ABRecordCopyValue(lPerson, kABPersonPhoneProperty); - if (lMap) { - for (int i = 0; i < ABMultiValueGetCount(lMap); i++) { - NSString *lValue = (__bridge NSString *)ABMultiValueCopyValueAtIndex(lMap, i); - char *normalizedPhone = linphone_proxy_config_normalize_phone_number( - linphone_core_get_default_proxy_config(LC), lValue.UTF8String); - NSString *name = [FastAddressBook - normalizeSipURI:normalizedPhone ? [NSString stringWithUTF8String:normalizedPhone] : lValue]; - [_addressBookMap setObject:(__bridge id)(lPerson) forKey:name ?: lValue]; - if (normalizedPhone) - ms_free(normalizedPhone); - CFRelease((CFStringRef)lValue); - } - CFRelease(lMap); - } - } - - // SIP - { - ABMultiValueRef lMap = ABRecordCopyValue(lPerson, kABPersonInstantMessageProperty); - if (lMap) { - for (int i = 0; i < ABMultiValueGetCount(lMap); ++i) { - CFDictionaryRef lDict = ABMultiValueCopyValueAtIndex(lMap, i); - BOOL add = false; - if (CFDictionaryContainsKey(lDict, kABPersonInstantMessageServiceKey)) { - if (CFStringCompare((CFStringRef)LinphoneManager.instance.contactSipField, - CFDictionaryGetValue(lDict, kABPersonInstantMessageServiceKey), - kCFCompareCaseInsensitive) == 0) { - add = true; - } - } else { - add = true; - } - if (add) { - NSString *lValue = - (__bridge NSString *)CFDictionaryGetValue(lDict, kABPersonInstantMessageUsernameKey); - NSString *lNormalizedKey = [FastAddressBook normalizeSipURI:lValue]; - if (lNormalizedKey != NULL) { - [_addressBookMap setObject:(__bridge id)(lPerson) forKey:lNormalizedKey]; - } else { - [_addressBookMap setObject:(__bridge id)(lPerson) forKey:lValue]; - } - } - CFRelease(lDict); - } - CFRelease(lMap); - } - } + Contact *contact = [[Contact alloc] initWithPerson:lPerson]; + [self registerAddrsFor:contact]; } CFRelease(lContacts); + + // load Linphone friends + const MSList *lists = linphone_core_get_friends_lists(LC); + while (lists) { + LinphoneFriendList *fl = lists->data; + const MSList *friends = linphone_friend_list_get_friends(fl); + while (friends) { + LinphoneFriend *f = friends->data; + Contact *contact = [[Contact alloc] initWithFriend:f]; + [self registerAddrsFor:contact]; + friends = friends->next; + } + lists = lists->next; + } } [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneAddressBookUpdate object:self]; } @@ -219,74 +191,39 @@ void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void return @""; } -+ (BOOL)contactHasValidSipDomain:(ABRecordRef)person { - if (person == nil) ++ (BOOL)contactHasValidSipDomain:(Contact *)contact { + if (contact == nil) return NO; // Check if one of the contact' sip URI matches the expected SIP filter - ABMultiValueRef personSipAddresses = ABRecordCopyValue(person, kABPersonInstantMessageProperty); - BOOL match = false; NSString *domain = LinphoneManager.instance.contactFilter; - for (int i = 0; i < ABMultiValueGetCount(personSipAddresses) && !match; ++i) { - CFDictionaryRef lDict = ABMultiValueCopyValueAtIndex(personSipAddresses, i); - if (CFDictionaryContainsKey(lDict, kABPersonInstantMessageServiceKey)) { - CFStringRef serviceKey = CFDictionaryGetValue(lDict, kABPersonInstantMessageServiceKey); - - if (CFStringCompare((CFStringRef)LinphoneManager.instance.contactSipField, serviceKey, - kCFCompareCaseInsensitive) == 0) { - match = true; - } - } else if (domain != nil) { - // check domain - LinphoneAddress *address = linphone_core_interpret_url( - LC, [(NSString *)CFDictionaryGetValue(lDict, kABPersonInstantMessageUsernameKey) UTF8String]); - - if (address) { - const char *dom = linphone_address_get_domain(address); - if (dom != NULL) { - NSString *contactDomain = - [NSString stringWithCString:dom encoding:[NSString defaultCStringEncoding]]; - - match = (([domain compare:@"*" options:NSCaseInsensitiveSearch] == NSOrderedSame) || - ([domain compare:contactDomain options:NSCaseInsensitiveSearch] == NSOrderedSame)); - } - linphone_address_destroy(address); + for (NSString *sip in contact.sipAddresses) { + // check domain + LinphoneAddress *address = linphone_core_interpret_url(LC, sip.UTF8String); + if (address) { + const char *dom = linphone_address_get_domain(address); + BOOL match = false; + if (dom != NULL) { + NSString *contactDomain = [NSString stringWithCString:dom encoding:[NSString defaultCStringEncoding]]; + match = (([domain compare:@"*" options:NSCaseInsensitiveSearch] == NSOrderedSame) || + ([domain compare:contactDomain options:NSCaseInsensitiveSearch] == NSOrderedSame)); } + linphone_address_destroy(address); + if (match) + return YES; } - CFRelease(lDict); } - CFRelease(personSipAddresses); - return match; + return NO; } -+ (NSString *)displayNameForContact:(ABRecordRef)contact { - NSString *ret = NSLocalizedString(@"Unknown", nil); - if (contact != nil) { - NSString *lFirstName = CFBridgingRelease(ABRecordCopyValue(contact, kABPersonFirstNameProperty)); - NSString *lLocalizedFirstName = [FastAddressBook localizedLabel:lFirstName]; - NSString *compositeName = CFBridgingRelease(ABRecordCopyCompositeName(contact)); - - NSString *lLastName = CFBridgingRelease(ABRecordCopyValue(contact, kABPersonLastNameProperty)); - NSString *lLocalizedLastName = [FastAddressBook localizedLabel:lLastName]; - - NSString *lOrganization = CFBridgingRelease(ABRecordCopyValue(contact, kABPersonOrganizationProperty)); - NSString *lLocalizedOrganization = [FastAddressBook localizedLabel:lOrganization]; - - if (compositeName) { - ret = compositeName; - } else if (lLocalizedFirstName || lLocalizedLastName) { - ret = [NSString stringWithFormat:@"%@ %@", lLocalizedFirstName, lLocalizedLastName]; - } else { - ret = (NSString *)lLocalizedOrganization; - } - } - return ret; ++ (NSString *)displayNameForContact:(Contact *)contact { + return contact.displayName; } + (NSString *)displayNameForAddress:(const LinphoneAddress *)addr { NSString *ret = NSLocalizedString(@"Unknown", nil); - ABRecordRef contact = [FastAddressBook getContactWithAddress:addr]; + Contact *contact = [FastAddressBook getContactWithAddress:addr]; if (contact) { ret = [FastAddressBook displayNameForContact:contact]; } else { @@ -301,11 +238,11 @@ void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void return ret; } -- (int)removeContact:(ABRecordRef)contact { +- (int)removeContact:(Contact *)contact { // Remove contact from book - if (contact && ABRecordGetRecordID(contact) != kABRecordInvalidID) { + if (contact.person && ABRecordGetRecordID(contact.person) != kABRecordInvalidID) { CFErrorRef error = NULL; - ABAddressBookRemoveRecord(addressBook, contact, (CFErrorRef *)&error); + ABAddressBookRemoveRecord(addressBook, contact.person, (CFErrorRef *)&error); if (error != NULL) { LOGE(@"Remove contact %p: Fail(%@)", contact, [(__bridge NSError *)error localizedDescription]); } else { @@ -328,4 +265,49 @@ void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void } return -2; } + +- (BOOL)saveContact:(Contact *)contact { + CFErrorRef error = NULL; + if (ABRecordGetRecordID(contact.person) == kABRecordInvalidID) { + ABAddressBookAddRecord(addressBook, contact.person, (CFErrorRef *)&error); + if (error != NULL) { + LOGE(@"Add contact %p: Fail(%@)", contact.person, [(__bridge NSError *)error localizedDescription]); + } else { + LOGI(@"Add contact %p: Success!", contact.person); + } + } + + // Save address book + error = NULL; + ABAddressBookSave(addressBook, &error); + if (error != NULL) { + LOGE(@"Save AddressBook: Fail(%@)", [(__bridge NSError *)error localizedDescription]); + } else { + LOGI(@"Save AddressBook: Success!"); + } + [self reload]; + + return error == NULL; +} + ++ (void)setAvatar:(UIImage *)avatar forContact:(Contact *)contact { + FastAddressBook *fab = LinphoneManager.instance.fastAddressBook; + CFErrorRef error = NULL; + if (!ABPersonRemoveImageData(contact.person, (CFErrorRef *)&error)) { + LOGI(@"Can't remove entry: %@", [(__bridge NSError *)error localizedDescription]); + } + NSData *dataRef = UIImageJPEGRepresentation(avatar, 0.9f); + CFDataRef cfdata = CFDataCreate(NULL, [dataRef bytes], [dataRef length]); + + [fab saveAddressBook]; + + if (!ABPersonSetImageData(contact.person, cfdata, (CFErrorRef *)&error)) { + LOGI(@"Can't add entry: %@", [(__bridge NSError *)error localizedDescription]); + } else { + [fab saveAddressBook]; + } + + CFRelease(cfdata); +} + @end diff --git a/Classes/Utils/Utils.h b/Classes/Utils/Utils.h index f0ed72d63..4fc336c75 100644 --- a/Classes/Utils/Utils.h +++ b/Classes/Utils/Utils.h @@ -71,7 +71,7 @@ typedef enum { @end @interface ContactDisplay : NSObject -+ (void)setDisplayNameLabel:(UILabel *)label forContact:(ABRecordRef)contact; ++ (void)setDisplayNameLabel:(UILabel *)label forContact:(Contact *)contact; + (void)setDisplayNameLabel:(UILabel *)label forAddress:(const LinphoneAddress *)addr; @end diff --git a/Classes/Utils/Utils.m b/Classes/Utils/Utils.m index 3ad1371c2..331b32b2e 100644 --- a/Classes/Utils/Utils.m +++ b/Classes/Utils/Utils.m @@ -514,7 +514,7 @@ @implementation ContactDisplay -+ (void)setDisplayNameLabel:(UILabel *)label forContact:(ABRecordRef)contact { ++ (void)setDisplayNameLabel:(UILabel *)label forContact:(Contact *)contact { label.text = [FastAddressBook displayNameForContact:contact]; #if 0 NSString *lLastName = CFBridgingRelease(ABRecordCopyValue(contact, kABPersonLastNameProperty)); @@ -526,7 +526,7 @@ } + (void)setDisplayNameLabel:(UILabel *)label forAddress:(const LinphoneAddress *)addr { - const LinphoneFriend *contact = [FastAddressBook getContactWithAddress:addr]; + Contact *contact = [FastAddressBook getContactWithAddress:addr]; if (contact) { [ContactDisplay setDisplayNameLabel:label forContact:contact]; } else { diff --git a/Resources/linphonerc-factory b/Resources/linphonerc-factory index a6f5c0768..a1ef9d0e7 100644 --- a/Resources/linphonerc-factory +++ b/Resources/linphonerc-factory @@ -27,3 +27,6 @@ ringer_dev_id=AQ: Audio Queue Device [video] display_filter_auto_rotate=0 + +[friend_0] +url="gautier.pelloux" \ No newline at end of file diff --git a/linphone_Prefix.pch b/linphone_Prefix.pch index e81415137..6f87a986f 100644 --- a/linphone_Prefix.pch +++ b/linphone_Prefix.pch @@ -9,6 +9,7 @@ #import "Log.h" #import "Utils.h" +#import "Contact.h" #import "UIToggleButton.h" #import "UISpeakerButton.h" diff --git a/submodules/bctoolbox b/submodules/bctoolbox index 0f2997200..e654c0082 160000 --- a/submodules/bctoolbox +++ b/submodules/bctoolbox @@ -1 +1 @@ -Subproject commit 0f29972001c023ead9041412c64d7f1ab46434f7 +Subproject commit e654c008283c942345ac20540981ba854d9138ba diff --git a/submodules/cmake-builder b/submodules/cmake-builder index d04ec58fa..e9664d1bf 160000 --- a/submodules/cmake-builder +++ b/submodules/cmake-builder @@ -1 +1 @@ -Subproject commit d04ec58fa4c162634d32cce9ebcd3bdc3d07aa89 +Subproject commit e9664d1bf5a2898635c3b29d0fc9a04ede221fc0 diff --git a/submodules/linphone b/submodules/linphone index 62703c016..36fd57c45 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit 62703c016c4a75755ba074eda9e4b7a58d1241cd +Subproject commit 36fd57c45b9b6ebbafbe57e5be4c5e2120a281cf