[CNContact] CNContact first implementation

This commit is contained in:
Brieuc Viel 2017-11-08 17:22:23 +01:00
parent c9abca2710
commit 24733a0615
14 changed files with 1184 additions and 1119 deletions

View file

@ -199,10 +199,10 @@ void assistant_activate_phone_number_link(LinphoneAccountCreator *creator, Linph
}
[PhoneMainView.instance popToView:DialerView.compositeViewDescription];
[[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneAddressBookUpdate object:NULL];
[LinphoneManager.instance.fastAddressBook reload];
} else {
[thiz showErrorPopup:resp];
}
[LinphoneManager.instance.fastAddressBook reloadAllContacts];
} else {
[thiz showErrorPopup:resp];
}
}
#pragma mark - other

View file

@ -412,10 +412,10 @@ static UICompositeViewDescription *compositeDescription = nil;
linphone_core_set_default_proxy_config(LC, new_config);
// reload address book to prepend proxy config domain to contacts' phone number
// todo: STOP doing that!
[[LinphoneManager.instance fastAddressBook] reload];
} else {
[self displayAssistantConfigurationError];
}
[[LinphoneManager.instance fastAddressBook] reloadAllContacts];
} else {
[self displayAssistantConfigurationError];
}
}
- (void)displayAssistantConfigurationError {
@ -1366,14 +1366,17 @@ void assistant_is_account_linked(LinphoneAccountCreator *creator, LinphoneAccoun
linphone_core_set_default_proxy_config(LC, config);
// reload address book to prepend proxy config domain to contacts' phone number
// todo: STOP doing that!
[[LinphoneManager.instance fastAddressBook] reload];
[PhoneMainView.instance changeCurrentView:DialerView.compositeViewDescription];
} else {
[self displayAssistantConfigurationError];
}
} else {
[self displayAssistantConfigurationError];
}
[[LinphoneManager.instance fastAddressBook]
reloadAllContacts];
[PhoneMainView.instance
changeCurrentView:
DialerView.compositeViewDescription];
} else {
[self displayAssistantConfigurationError];
}
} else {
[self displayAssistantConfigurationError];
}
});
}

View file

@ -6,28 +6,31 @@
//
//
#import <Foundation/Foundation.h>
#import <AddressBook/AddressBook.h>
#import <Contacts/Contacts.h>
#import <Foundation/Foundation.h>
#include <linphone/linphonecore.h>
@interface Contact : NSObject
@property(nonatomic, readonly) ABRecordRef person;
//@property(nonatomic, readonly) ABRecordRef person;
@property(nonatomic, readonly) CNContact *person;
@property(nonatomic, readonly) LinphoneFriend *friend;
@property(nonatomic, retain) NSString *identifier;
@property(nonatomic, retain) NSString *firstName;
@property(nonatomic, retain) NSString *lastName;
@property(nonatomic, retain) NSString *displayName;
@property(nonatomic, strong) NSMutableArray *sipAddresses;
@property(nonatomic, strong) NSMutableArray *emails;
@property(nonatomic, strong) NSMutableArray *phoneNumbers;
@property(nonatomic, strong) NSMutableArray *phones;
@property BOOL added;
- (void)setAvatar:(UIImage *)avatar;
- (UIImage *)avatar;
- (NSString *)displayName;
- (instancetype)initWithPerson:(ABRecordRef)person;
- (instancetype)initWithCNContact:(CNContact *)contact;
- (instancetype)initWithFriend:(LinphoneFriend *) friend;
- (BOOL)setSipAddress:(NSString *)sip atIndex:(NSInteger)index;

File diff suppressed because it is too large Load diff

View file

@ -29,15 +29,17 @@
- (NSMutableArray *)getSectionData:(NSInteger)section {
if (section == ContactSections_Number) {
return _contact.phoneNumbers;
} else if (section == ContactSections_Sip) {
return _contact.sipAddresses;
} else if (section == ContactSections_Email) {
if ([LinphoneManager.instance lpConfigBoolForKey:@"show_contacts_emails_preference"] == true) {
return _contact.emails;
}
}
return nil;
return _contact.phones;
} else if (section == ContactSections_Sip) {
return _contact.sipAddresses;
} else if (section == ContactSections_Email) {
if ([LinphoneManager.instance
lpConfigBoolForKey:@"show_contacts_emails_preference"] ==
true) {
return _contact.emails;
}
}
return nil;
}
- (void)removeEmptyEntry:(UITableView *)tableview section:(NSInteger)section animated:(BOOL)animated {
@ -75,25 +77,30 @@
if (section == ContactSections_Number) {
added = [_contact addPhoneNumber:value];
} else if (section == ContactSections_Sip) {
added = [_contact addSipAddress:value];
} else if (section == ContactSections_Email) {
added = [_contact addEmail:value];
}
if (added) {
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);
}
if ([_contact.sipAddresses count] ==
[_contact.person.instantMessageAddresses count])
added = [_contact addSipAddress:value];
} else if (section == ContactSections_Email) {
added = [_contact addEmail:value];
}
if (added) {
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);
}
}
- (void)setContact:(Contact *)acontact {
if (acontact == _contact)
return;
_contact = acontact;
[self loadData];
// if (acontact == _contact)
// return;
_contact = acontact;
[self loadData];
}
- (void)addPhoneField:(NSString *)number {
@ -119,8 +126,9 @@
- (BOOL)isValid {
BOOL hasName = (_contact.firstName.length + _contact.lastName.length > 0);
BOOL hasAddr = (_contact.phoneNumbers.count + _contact.sipAddresses.count) > 0;
return hasName && hasAddr;
BOOL hasAddr =
(_contact.phones.count + _contact.sipAddresses.count) > 0;
return hasName && hasAddr;
}
#pragma mark - UITableViewDataSource Functions
@ -140,12 +148,13 @@
} else if (section == ContactSections_Sip) {
return _contact.sipAddresses.count;
} else if (section == ContactSections_Number) {
return _contact.phoneNumbers.count;
} else if (section == ContactSections_Email) {
BOOL showEmails = [LinphoneManager.instance lpConfigBoolForKey:@"show_contacts_emails_preference"];
return showEmails ? _contact.emails.count : 0;
}
return 0;
return _contact.phones.count;
} else if (section == ContactSections_Email) {
BOOL showEmails = [LinphoneManager.instance
lpConfigBoolForKey:@"show_contacts_emails_preference"];
return showEmails ? _contact.emails.count : 0;
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
@ -169,26 +178,28 @@
value = _contact.lastName;
[cell hideDeleteButton:YES];
} else if ([indexPath section] == ContactSections_Number) {
value = _contact.phoneNumbers[indexPath.row];
[cell.editTextfield setKeyboardType:UIKeyboardTypePhonePad];
} else if ([indexPath section] == ContactSections_Sip) {
value = _contact.sipAddresses[indexPath.row];
LinphoneAddress *addr = NULL;
if ([LinphoneManager.instance lpConfigBoolForKey:@"contact_display_username_only"] &&
(addr = linphone_core_interpret_url(LC, [value UTF8String]))) {
value = [NSString stringWithCString:linphone_address_get_username(addr)
encoding:[NSString defaultCStringEncoding]];
linphone_address_destroy(addr);
}
[cell.editTextfield setKeyboardType:UIKeyboardTypeASCIICapable];
} else if ([indexPath section] == ContactSections_Email) {
value = _contact.emails[indexPath.row];
[cell.editTextfield setKeyboardType:UIKeyboardTypeEmailAddress];
}
value = _contact.phones[indexPath.row];
[cell.editTextfield setKeyboardType:UIKeyboardTypePhonePad];
} else if ([indexPath section] == ContactSections_Sip) {
value = _contact.sipAddresses[indexPath.row];
LinphoneAddress *addr = NULL;
if ([LinphoneManager.instance
lpConfigBoolForKey:@"contact_display_username_only"] &&
(addr = linphone_core_interpret_url(LC, [value UTF8String]))) {
value =
[NSString stringWithCString:linphone_address_get_username(addr)
encoding:[NSString defaultCStringEncoding]];
linphone_address_destroy(addr);
}
[cell.editTextfield setKeyboardType:UIKeyboardTypeASCIICapable];
} else if ([indexPath section] == ContactSections_Email) {
value = _contact.emails[indexPath.row];
[cell.editTextfield setKeyboardType:UIKeyboardTypeEmailAddress];
}
[cell setAddress:value];
[cell setAddress:value];
return cell;
return cell;
}
- (void)tableView:(UITableView *)tableView
@ -197,13 +208,16 @@
[LinphoneUtils findAndResignFirstResponder:[self tableView]];
if (editingStyle == UITableViewCellEditingStyleInsert) {
[tableView beginUpdates];
[self addEntry:tableView section:[indexPath section] animated:TRUE value:@""];
[tableView endUpdates];
} else if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
[self removeEntry:tableView indexPath:indexPath animated:TRUE];
[tableView endUpdates];
}
[self addEntry:tableView
section:[indexPath section]
animated:TRUE
value:@" "];
[tableView endUpdates];
} else if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
[self removeEntry:tableView indexPath:indexPath animated:TRUE];
[tableView endUpdates];
}
}
#pragma mark - UITableViewDelegate Functions
@ -371,19 +385,22 @@
break;
case ContactSections_Number:
[_contact setPhoneNumber:value atIndex:path.row];
value = _contact.phoneNumbers[path.row]; // in case of reformatting
break;
case ContactSections_MAX:
case ContactSections_None:
break;
}
cell.editTextfield.text = value;
_editButton.enabled = [self isValid];
}
value =
_contact.phones[path.row]; // in case of
// reformatting
break;
case ContactSections_MAX:
case ContactSections_None:
break;
}
cell.editTextfield.text = value;
_editButton.enabled = [self isValid];
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self textFieldUpdated:textField];
// TODO reload current contact
}
- (BOOL)textField:(UITextField *)textField

View file

@ -29,12 +29,18 @@
self = [super initWithNibName:NSStringFromClass(self.class) bundle:[NSBundle mainBundle]];
if (self != nil) {
inhibUpdate = FALSE;
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(onAddressBookUpdate:)
name:kLinphoneAddressBookUpdate
object:nil];
}
return self;
[NSNotificationCenter.defaultCenter
addObserver:self
selector:@selector(onAddressBookUpdate:)
name:kLinphoneAddressBookUpdate
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(onAddressBookUpdate:)
name:CNContactStoreDidChangeNotification
object:nil];
}
return self;
}
- (void)dealloc {
@ -44,11 +50,12 @@
#pragma mark -
- (void)onAddressBookUpdate:(NSNotification *)k {
if (!inhibUpdate && ![_tableController isEditing] &&
(PhoneMainView.instance.currentView == self.compositeViewDescription) &&
(_nameLabel.text == PhoneMainView.instance.currentName)) {
[self resetData];
}
if (!inhibUpdate && ![_tableController isEditing] &&
(PhoneMainView.instance.currentView == self.compositeViewDescription) &&
(_nameLabel.text == PhoneMainView.instance.currentName)) {
[self resetData];
}
}
- (void)resetData {
@ -67,9 +74,9 @@
- (void)removeContact {
inhibUpdate = TRUE;
[[LinphoneManager.instance fastAddressBook] removeContact:_contact];
inhibUpdate = FALSE;
[PhoneMainView.instance popCurrentView];
[[LinphoneManager.instance fastAddressBook] deleteContact:_contact];
inhibUpdate = FALSE;
[PhoneMainView.instance popCurrentView];
}
- (void)saveData {
@ -77,9 +84,9 @@
[PhoneMainView.instance popCurrentView];
return;
}
// Add contact to book
[LinphoneManager.instance.fastAddressBook saveContact:_contact];
PhoneMainView.instance.currentName = _contact.displayName;
_nameLabel.text = PhoneMainView.instance.currentName;
[LinphoneManager.instance.fastAddressBook saveContact:_contact];
}
- (void)selectContact:(Contact *)acontact andReload:(BOOL)reload {
@ -109,12 +116,11 @@
if (!acontact) {
return;
}
_tmpContact = [[Contact alloc] initWithPerson:ABPersonCreate()];
_tmpContact.firstName = acontact.firstName.copy;
_tmpContact.lastName = acontact.lastName.copy;
_tmpContact.sipAddresses = acontact.sipAddresses.copy;
_tmpContact.emails = acontact.emails.copy;
_tmpContact.phoneNumbers = acontact.phoneNumbers.copy;
@synchronized(LinphoneManager.instance.fastAddressBook) {
_tmpContact = [[Contact alloc]
initWithCNContact:[LinphoneManager.instance.fastAddressBook
getCNContactFromContact:acontact]];
}
}
- (void)addCurrentContactContactField:(NSString *)address {
@ -140,14 +146,19 @@
- (void)newContact {
_isAdding = TRUE;
[self selectContact:[[Contact alloc] initWithPerson:ABPersonCreate()] andReload:YES];
CNContact *contact = [[CNContact alloc] init];
[self selectContact:[[Contact alloc] initWithCNContact:contact]
andReload:YES];
}
- (void)newContact:(NSString *)address {
[self selectContact:[[Contact alloc] initWithPerson:ABPersonCreate()] andReload:NO];
[self addCurrentContactContactField:address];
// force to restart server subscription to add new contact into the list
[LinphoneManager.instance becomeActive];
CNContact *contact = [[CNContact alloc] init];
Contact *mContact = [[Contact alloc] initWithCNContact:contact];
[mContact setSipAddress:address atIndex:0];
[self selectContact:mContact andReload:NO];
[self addCurrentContactContactField:address];
// force to restart server subscription to add new contact into the list
[LinphoneManager.instance becomeActive];
}
- (void)editContact:(Contact *)acontact {
@ -256,47 +267,45 @@
[_contact addSipAddress:_tmpContact.sipAddresses[nbSipAd]];
nbSipAd++;
}
while (_contact.phoneNumbers.count > 0) {
[_contact removePhoneNumberAtIndex:0];
}
NSInteger nbPhone = 0;
while (_tmpContact.phoneNumbers.count> nbPhone) {
[_contact addPhoneNumber:_tmpContact.phoneNumbers[nbPhone]];
nbPhone++;
}
while (_contact.emails.count > 0) {
[_contact removeEmailAtIndex:0];
}
NSInteger nbEmail = 0;
while (_tmpContact.emails.count> nbEmail) {
[_contact addEmail:_tmpContact.emails[nbEmail]];
nbEmail++;
}
self.tmpContact = NULL;
[self saveData];
}
BOOL rm = TRUE;
for (NSString *sip in _contact.sipAddresses) {
if (![sip isEqualToString:@""]) {
rm = FALSE;
break;
}
}
if (rm) {
for (NSString *phone in _contact.phoneNumbers) {
if (![phone isEqualToString:@""]) {
rm = FALSE;
break;
}
}
}
if (rm) {
[LinphoneManager.instance.fastAddressBook removeContact:_contact];
}
while (_contact.phones.count > 0) {
[_contact removePhoneNumberAtIndex:0];
}
NSInteger nbPhone = 0;
while (_tmpContact.phones.count > nbPhone) {
[_contact addPhoneNumber:_tmpContact.phones[nbPhone]];
nbPhone++;
}
while (_contact.emails.count > 0) {
[_contact removeEmailAtIndex:0];
}
NSInteger nbEmail = 0;
while (_tmpContact.emails.count > nbEmail) {
[_contact addEmail:_tmpContact.emails[nbEmail]];
nbEmail++;
}
self.tmpContact = NULL;
[self saveData];
}
BOOL rm = TRUE;
for (NSString *sip in _contact.sipAddresses) {
if (![sip isEqualToString:@""]) {
rm = FALSE;
break;
}
}
if (rm) {
for (NSString *phone in _contact.phones) {
if (![phone isEqualToString:@""]) {
rm = FALSE;
break;
}
}
}
if (rm) {
[LinphoneManager.instance.fastAddressBook deleteContact:_contact];
}
}
#pragma mark - UICompositeViewDelegate Functions
@ -397,55 +406,63 @@ static UICompositeViewDescription *compositeDescription = nil;
[_contact removeSipAddressAtIndex:0];
}
NSInteger nbSipAd = 0;
while (_tmpContact.sipAddresses.count > nbSipAd) {
[_contact addSipAddress:_tmpContact.sipAddresses[nbSipAd]];
nbSipAd++;
}
while (_contact.phoneNumbers.count > 0) {
[_contact removePhoneNumberAtIndex:0];
}
NSInteger nbPhone = 0;
while (_tmpContact.phoneNumbers.count> nbPhone) {
[_contact addPhoneNumber:_tmpContact.phoneNumbers[nbPhone]];
nbPhone++;
}
while (_contact.emails.count > 0) {
[_contact removeEmailAtIndex:0];
}
NSInteger nbEmail = 0;
while (_tmpContact.emails.count> nbEmail) {
[_contact addEmail:_tmpContact.emails[nbEmail]];
nbEmail++;
}
[self saveData];
[self.tableController.tableView reloadData];
} else {
[LinphoneManager.instance.fastAddressBook removeContact:_contact];
}
[self setEditing:FALSE];
if (IPAD) {
_emptyLabel.hidden = !_isAdding;
_avatarImage.hidden = !_emptyLabel.hidden;
_deleteButton.hidden = !_emptyLabel.hidden;
_editButton.hidden = !_emptyLabel.hidden;
} else {
if (_isAdding) {
[PhoneMainView.instance popCurrentView];
} else {
_avatarImage.hidden = FALSE;
_deleteButton.hidden = FALSE;
_editButton.hidden = FALSE;
}
}
self.tmpContact = NULL;
if (_isAdding) {
[PhoneMainView.instance popToView:ContactsListView.compositeViewDescription];
_isAdding = FALSE;
}
if (_tmpContact.sipAddresses) {
while (_tmpContact.sipAddresses.count > nbSipAd) {
[_contact addSipAddress:_tmpContact.sipAddresses[nbSipAd]];
nbSipAd++;
}
}
while (_contact.phones.count > 0 &&
_contact.phones[0] != NULL) {
[_contact removePhoneNumberAtIndex:0];
}
NSInteger nbPhone = 0;
if (_tmpContact.phones != NULL) {
while (_tmpContact.phones.count > nbPhone) {
[_contact addPhoneNumber:_tmpContact.phones[nbPhone]];
nbPhone++;
}
}
while (_contact.emails.count > 0) {
[_contact removeEmailAtIndex:0];
}
NSInteger nbEmail = 0;
if (_tmpContact.emails != NULL) {
while (_tmpContact.emails.count > nbEmail) {
[_contact addEmail:_tmpContact.emails[nbEmail]];
// [_contact
// addPhoneNumber:((CNLabeledValue<CNPhoneNumber*>*)_tmpContact.phoneNumbers[nbPhone]).value.stringValue];
nbEmail++;
}
}
[self saveData];
//[self.tableController.tableView reloadData];
} else {
[LinphoneManager.instance.fastAddressBook deleteContact:_contact];
}
[self setEditing:FALSE];
if (IPAD) {
_emptyLabel.hidden = !_isAdding;
_avatarImage.hidden = !_emptyLabel.hidden;
_deleteButton.hidden = !_emptyLabel.hidden;
_editButton.hidden = !_emptyLabel.hidden;
} else {
if (_isAdding) {
[PhoneMainView.instance popCurrentView];
} else {
_avatarImage.hidden = FALSE;
_deleteButton.hidden = FALSE;
_editButton.hidden = FALSE;
}
}
self.tmpContact = NULL;
if (_isAdding) {
[PhoneMainView.instance
popToView:ContactsListView.compositeViewDescription];
_isAdding = FALSE;
}
}
- (IBAction)onBackClick:(id)event {

View file

@ -29,10 +29,16 @@
- (void)initContactsTableViewController {
addressBookMap = [[OrderedDictionary alloc] init];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(onAddressBookUpdate:)
name:kLinphoneAddressBookUpdate
object:nil];
[NSNotificationCenter.defaultCenter
addObserver:self
selector:@selector(onAddressBookUpdate:)
name:kLinphoneAddressBookUpdate
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(onAddressBookUpdate:)
name:CNContactStoreDidChangeNotification
object:nil];
}
- (void)onAddressBookUpdate:(NSNotification *)k {
@ -121,6 +127,7 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) {
return nil;
}
// TODO - check this
- (void)loadData {
_ongoing = TRUE;
LOGI(@"Load contact list");
@ -136,67 +143,88 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) {
// Reset Address book
[addressBookMap removeAllObjects];
for (NSString *addr in LinphoneManager.instance.fastAddressBook.addressBookMap) {
Contact *contact = [LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:addr];
BOOL add = true;
@synchronized(
LinphoneManager.instance.fastAddressBook.addressBookMap) {
Contact *contact =
[LinphoneManager.instance.fastAddressBook.addressBookMap
objectForKey:addr];
BOOL add = true;
// Do not add the contact directly if we set some filter
if ([ContactSelection getSipFilter] ||
[ContactSelection emailFilterEnabled]) {
add = false;
}
if ([FastAddressBook contactHasValidSipDomain:contact]) {
add = true;
}
if (contact.friend &&
linphone_presence_model_get_basic_status(
linphone_friend_get_presence_model(
contact.friend)) ==
LinphonePresenceBasicStatusOpen) {
add = true;
}
// Do not add the contact directly if we set some filter
if ([ContactSelection getSipFilter] || [ContactSelection emailFilterEnabled]) {
add = false;
}
if ([FastAddressBook contactHasValidSipDomain:contact]) {
add = true;
}
if (contact.friend && linphone_presence_model_get_basic_status(linphone_friend_get_presence_model(contact.friend)) == LinphonePresenceBasicStatusOpen){
add = true;
}
if (!add && [ContactSelection emailFilterEnabled]) {
// Add this contact if it has an email
add = (contact.emails.count > 0);
}
if (!add && [ContactSelection emailFilterEnabled]) {
// Add this contact if it has an email
add = (contact.emails.count > 0);
}
NSMutableString *name = [self displayNameForContact:contact]
? [[NSMutableString alloc] initWithString:[self displayNameForContact:contact]]
: nil;
if (add && name != nil) {
NSString *firstChar = [[name substringToIndex:1] uppercaseString];
NSMutableString *name =
[self displayNameForContact:contact]
? [[NSMutableString alloc]
initWithString:
[self displayNameForContact:contact]]
: nil;
if (add && name != nil) {
NSString *firstChar =
[[name substringToIndex:1] uppercaseString];
// Put in correct subAr
if ([firstChar characterAtIndex:0] < 'A' || [firstChar characterAtIndex:0] > 'Z') {
firstChar = @"#";
}
NSMutableArray *subAr = [addressBookMap objectForKey:firstChar];
if (subAr == nil) {
subAr = [[NSMutableArray alloc] init];
[addressBookMap insertObject:subAr forKey:firstChar selector:@selector(caseInsensitiveCompare:)];
}
NSUInteger idx = [subAr indexOfObject:contact
inSortedRange:(NSRange){0, subAr.count}
options:NSBinarySearchingInsertionIndex
usingComparator:^NSComparisonResult(Contact* _Nonnull obj1, Contact* _Nonnull obj2) {
return [[self displayNameForContact:obj1] compare:[self displayNameForContact:obj2]options:NSCaseInsensitiveSearch];
}];
if (![subAr containsObject:contact]) {
[subAr insertObject:contact atIndex:idx];
}
}
}
[super loadData];
// Put in correct subAr
if ([firstChar characterAtIndex:0] < 'A' ||
[firstChar characterAtIndex:0] > 'Z') {
firstChar = @"#";
}
NSMutableArray *subAr =
[addressBookMap objectForKey:firstChar];
if (subAr == nil) {
subAr = [[NSMutableArray alloc] init];
[addressBookMap
insertObject:subAr
forKey:firstChar
selector:@selector(caseInsensitiveCompare:)];
}
NSUInteger idx = [subAr
indexOfObject:contact
inSortedRange:(NSRange){0, subAr.count}
options:NSBinarySearchingInsertionIndex
usingComparator:^NSComparisonResult(
Contact *_Nonnull obj1, Contact *_Nonnull obj2) {
return [[self displayNameForContact:obj1]
compare:[self displayNameForContact:obj2]
options:NSCaseInsensitiveSearch];
}];
if (![subAr containsObject:contact]) {
[subAr insertObject:contact atIndex:idx];
}
}
}
}
[super loadData];
// since we refresh the tableview, we must perform this on main thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
if (IPAD) {
if (!([self totalNumberOfItems] > 0)) {
ContactDetailsView *view = VIEW(ContactDetailsView);
[view setContact:nil];
}
}
});
}
_ongoing = FALSE;
// since we refresh the tableview, we must perform this on main
// thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
if (IPAD) {
if (!([self totalNumberOfItems] > 0)) {
ContactDetailsView *view = VIEW(ContactDetailsView);
[view setContact:nil];
}
}
});
}
_ongoing = FALSE;
}
- (void)loadSearchedData {
@ -218,66 +246,94 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) {
NSMutableArray *subArContain = [NSMutableArray new];
[addressBookMap insertObject:subAr forKey:@"" selector:@selector(caseInsensitiveCompare:)];
for (NSString *addr in LinphoneManager.instance.fastAddressBook.addressBookMap) {
Contact *contact = [LinphoneManager.instance.fastAddressBook.addressBookMap objectForKey:addr];
BOOL add = true;
// Do not add the contact directly if we set some filter
if ([ContactSelection getSipFilter] || [ContactSelection emailFilterEnabled]) {
add = false;
}
NSString* filter = [ContactSelection getNameOrEmailFilter];
if ([FastAddressBook contactHasValidSipDomain:contact]) {
add = true;
}
if (contact.friend && linphone_presence_model_get_basic_status(linphone_friend_get_presence_model(contact.friend)) == LinphonePresenceBasicStatusOpen){
add = true;
}
if (!add && [ContactSelection emailFilterEnabled]) {
// Add this contact if it has an email
add = (contact.emails.count > 0);
}
NSInteger idx_begin = -1;
NSInteger idx_sort = - 1;
NSMutableString *name = [self displayNameForContact:contact]
? [[NSMutableString alloc] initWithString:[self displayNameForContact:contact]]
: nil;
if (add && name != nil) {
if ([[contact displayName] rangeOfString:filter options:NSCaseInsensitiveSearch].location == 0) {
if(![subArBegin containsObject:contact]) {
idx_begin = idx_begin + 1;
[subArBegin insertObject:contact atIndex:idx_begin];
}
} else if([[contact displayName] rangeOfString:filter options:NSCaseInsensitiveSearch].location != NSNotFound) {
if(![subArContain containsObject:contact]) {
idx_sort = idx_sort + 1;
[subArContain insertObject:contact atIndex:idx_sort];
}
}
}
}
[subArBegin sortUsingComparator:^NSComparisonResult(Contact* _Nonnull obj1, Contact* _Nonnull obj2) {
return [[self displayNameForContact:obj1] compare:[self displayNameForContact:obj2]options:NSCaseInsensitiveSearch];
}];
[subArContain sortUsingComparator:^NSComparisonResult(Contact* _Nonnull obj1, Contact* _Nonnull obj2) {
return [[self displayNameForContact:obj1] compare:[self displayNameForContact:obj2]options:NSCaseInsensitiveSearch];
}];
@synchronized(
LinphoneManager.instance.fastAddressBook.addressBookMap) {
Contact *contact =
[LinphoneManager.instance.fastAddressBook.addressBookMap
objectForKey:addr];
[subAr addObjectsFromArray:subArBegin];
[subAr addObjectsFromArray:subArContain];
[super loadData];
// since we refresh the tableview, we must perform this on main thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
if (IPAD) {
if (!([self totalNumberOfItems] > 0)) {
ContactDetailsView *view = VIEW(ContactDetailsView);
[view setContact:nil];
}
}
});
}
BOOL add = true;
// Do not add the contact directly if we set some filter
if ([ContactSelection getSipFilter] ||
[ContactSelection emailFilterEnabled]) {
add = false;
}
NSString *filter = [ContactSelection getNameOrEmailFilter];
if ([FastAddressBook contactHasValidSipDomain:contact]) {
add = true;
}
if (contact.friend &&
linphone_presence_model_get_basic_status(
linphone_friend_get_presence_model(
contact.friend)) ==
LinphonePresenceBasicStatusOpen) {
add = true;
}
if (!add && [ContactSelection emailFilterEnabled]) {
// Add this contact if it has an email
add = (contact.emails.count > 0);
}
NSInteger idx_begin = -1;
NSInteger idx_sort = -1;
NSMutableString *name =
[self displayNameForContact:contact]
? [[NSMutableString alloc]
initWithString:
[self displayNameForContact:contact]]
: nil;
if (add && name != nil) {
if ([[contact displayName]
rangeOfString:filter
options:NSCaseInsensitiveSearch]
.location == 0) {
if (![subArBegin containsObject:contact]) {
idx_begin = idx_begin + 1;
[subArBegin insertObject:contact atIndex:idx_begin];
}
} else if ([[contact displayName]
rangeOfString:filter
options:NSCaseInsensitiveSearch]
.location != NSNotFound) {
if (![subArContain containsObject:contact]) {
idx_sort = idx_sort + 1;
[subArContain insertObject:contact atIndex:idx_sort];
}
}
}
}
}
[subArBegin
sortUsingComparator:^NSComparisonResult(
Contact *_Nonnull obj1, Contact *_Nonnull obj2) {
return [[self displayNameForContact:obj1]
compare:[self displayNameForContact:obj2]
options:NSCaseInsensitiveSearch];
}];
[subArContain
sortUsingComparator:^NSComparisonResult(
Contact *_Nonnull obj1, Contact *_Nonnull obj2) {
return [[self displayNameForContact:obj1]
compare:[self displayNameForContact:obj2]
options:NSCaseInsensitiveSearch];
}];
[subAr addObjectsFromArray:subArBegin];
[subAr addObjectsFromArray:subArContain];
[super loadData];
// since we refresh the tableview, we must perform this on main
// thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
if (IPAD) {
if (!([self totalNumberOfItems] > 0)) {
ContactDetailsView *view = VIEW(ContactDetailsView);
[view setContact:nil];
}
}
});
}
}
@ -365,17 +421,20 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) {
}
UIContactCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
[cell setContact:NULL];
[[LinphoneManager.instance fastAddressBook] removeContact:contact];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
[[LinphoneManager.instance fastAddressBook]
deleteContact:contact];
[tableView
deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(onAddressBookUpdate:)
name:kLinphoneAddressBookUpdate
object:nil];
[self loadData];
}
[NSNotificationCenter.defaultCenter
addObserver:self
selector:@selector(onAddressBookUpdate:)
name:kLinphoneAddressBookUpdate
object:nil];
[self loadData];
}
}
- (void)removeSelectionUsing:(void (^)(NSIndexPath *))remover {
@ -391,12 +450,13 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) {
}
UIContactCell* cell = [self.tableView cellForRowAtIndexPath:indexPath];
[cell setContact:NULL];
[[LinphoneManager.instance fastAddressBook] removeContact:contact];
[[LinphoneManager.instance fastAddressBook] deleteContact:contact];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(onAddressBookUpdate:)
name:kLinphoneAddressBookUpdate
object:nil];
[NSNotificationCenter.defaultCenter
addObserver:self
selector:@selector(onAddressBookUpdate:)
name:kLinphoneAddressBookUpdate
object:nil];
}];
}

View file

@ -91,47 +91,56 @@
if (PhoneMainView.instance.currentView == ContactsListView.compositeViewDescription || PhoneMainView.instance.currentView == ContactDetailsView.compositeViewDescription) {
[PhoneMainView.instance changeCurrentView:DialerView.compositeViewDescription];
}
[instance.fastAddressBook reload];
instance.fastAddressBook.needToUpdate = FALSE;
const MSList *lists = linphone_core_get_friends_lists(LC);
while (lists) {
linphone_friend_list_update_subscriptions(lists->data);
lists = lists->next;
}
}
[instance.fastAddressBook reloadAllContacts];
instance.fastAddressBook.needToUpdate = FALSE;
const MSList *lists = linphone_core_get_friends_lists(LC);
while (lists) {
linphone_friend_list_update_subscriptions(lists->data);
lists = lists->next;
}
}
LinphoneCall *call = linphone_core_get_current_call(LC);
LinphoneCall *call = linphone_core_get_current_call(LC);
if (call) {
if (call == instance->currentCallContextBeforeGoingBackground.call) {
const LinphoneCallParams *params = linphone_call_get_current_params(call);
if (linphone_call_params_video_enabled(params)) {
linphone_call_enable_camera(call, instance->currentCallContextBeforeGoingBackground.cameraIsEnabled);
}
instance->currentCallContextBeforeGoingBackground.call = 0;
} else if (linphone_call_get_state(call) == LinphoneCallIncomingReceived) {
LinphoneCallAppData *data = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
if (data && data->timer) {
[data->timer invalidate];
data->timer = nil;
}
if ((floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max)) {
if ([LinphoneManager.instance lpConfigBoolForKey:@"autoanswer_notif_preference"]) {
linphone_call_accept(call);
[PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
} else {
[PhoneMainView.instance displayIncomingCall:call];
}
} else if (linphone_core_get_calls_nb(LC) > 1) {
[PhoneMainView.instance displayIncomingCall:call];
}
if (call) {
if (call == instance->currentCallContextBeforeGoingBackground.call) {
const LinphoneCallParams *params =
linphone_call_get_current_params(call);
if (linphone_call_params_video_enabled(params)) {
linphone_call_enable_camera(
call, instance->currentCallContextBeforeGoingBackground
.cameraIsEnabled);
}
instance->currentCallContextBeforeGoingBackground.call = 0;
} else if (linphone_call_get_state(call) ==
LinphoneCallIncomingReceived) {
LinphoneCallAppData *data =
(__bridge LinphoneCallAppData *)linphone_call_get_user_data(
call);
if (data && data->timer) {
[data->timer invalidate];
data->timer = nil;
}
if ((floor(NSFoundationVersionNumber) <=
NSFoundationVersionNumber_iOS_9_x_Max)) {
if ([LinphoneManager.instance
lpConfigBoolForKey:@"autoanswer_notif_preference"]) {
linphone_call_accept(call);
[PhoneMainView.instance
changeCurrentView:CallView.compositeViewDescription];
} else {
[PhoneMainView.instance displayIncomingCall:call];
}
} else if (linphone_core_get_calls_nb(LC) > 1) {
[PhoneMainView.instance displayIncomingCall:call];
}
// in this case, the ringing sound comes from the notification.
// To stop it we have to do the iOS7 ring fix...
[self fixRing];
}
}
[LinphoneManager.instance.iapManager check];
// in this case, the ringing sound comes from the notification.
// To stop it we have to do the iOS7 ring fix...
[self fixRing];
}
}
[LinphoneManager.instance.iapManager check];
}
#pragma deploymate push "ignored-api-availability"
@ -959,7 +968,7 @@ didInvalidatePushTokenForType:(NSString *)type {
linphone_core_set_provisioning_uri(LC, [configURL UTF8String]);
[LinphoneManager.instance destroyLinphoneCore];
[LinphoneManager.instance startLinphoneCore];
[LinphoneManager.instance.fastAddressBook reload];
[LinphoneManager.instance.fastAddressBook reloadAllContacts];
}
#pragma mark - Prevent ImagePickerView from rotating

View file

@ -636,7 +636,7 @@
}
}
// reload address book to prepend proxy config domain to contacts' phone number
[[LinphoneManager.instance fastAddressBook] reload];
[[LinphoneManager.instance fastAddressBook] reloadAllContacts];
}
- (void)synchronizeCodecs:(const MSList *)codecs {

View file

@ -2166,10 +2166,10 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat
[self destroyLinphoneCore];
[self createLinphoneCore];
// reload friends
[self.fastAddressBook reload];
[self.fastAddressBook reloadAllContacts];
// reset network state to trigger a new network connectivity assessment
linphone_core_set_network_reachable(theLinphoneCore, FALSE);
// reset network state to trigger a new network connectivity assessment
linphone_core_set_network_reachable(theLinphoneCore, FALSE);
}
static int comp_call_id(const LinphoneCall *call, const char *callid) {

View file

@ -28,16 +28,20 @@
@property(readonly, nonatomic) NSMutableDictionary *addressBookMap;
@property BOOL needToUpdate;
- (void)reload;
- (void)saveAddressBook;
- (int)removeContact:(Contact *)contact;
- (BOOL)reloadAllContacts;
//- (void)saveAddressBook;
- (BOOL)deleteContact:(Contact *)contact;
- (BOOL)deleteCNContact:(CNContact *)CNContact;
- (BOOL)deleteAllContacts;
- (BOOL)saveContact:(Contact *)contact;
- (BOOL)saveCNContact:(CNContact *)CNContact contact:(Contact *)Contact;
+ (BOOL)isAuthorized;
// TOOLS
+ (Contact *)getContactWithAddress:(const LinphoneAddress *)address;
- (CNContact *)getCNContactFromContact:(Contact *)acontact;
+ (UIImage *)imageForContact:(Contact *)contact;
+ (UIImage *)imageForAddress:(const LinphoneAddress *)addr;
@ -52,5 +56,6 @@
+ (NSString *)normalizeSipURI:(NSString *)address; // should be removed
+ (NSString *)localizedLabel:(NSString *)label;
- (void)registerAddrsFor:(Contact *)contact;
@end

View file

@ -25,11 +25,11 @@
#import "Utils.h"
@implementation FastAddressBook {
ABAddressBookRef addressBook;
CNContactStore* store;
}
static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void *context);
// static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef
// info, void *context);
+ (UIImage *)imageForContact:(Contact *)contact {
@synchronized(LinphoneManager.instance.fastAddressBook.addressBookMap) {
@ -108,150 +108,171 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info
}
+ (BOOL)isAuthorized {
return ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized;
return [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
}
- (FastAddressBook *)init {
if ((self = [super init]) != nil) {
_addressBookMap = [NSMutableDictionary dictionary];
addressBook = nil;
[self reload];
}
self.needToUpdate = FALSE;
if ([CNContactStore class]) {
//ios9 or later
CNEntityType entityType = CNEntityTypeContacts;
if([CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusNotDetermined) {
CNContactStore * contactStore = [[CNContactStore alloc] init];
LOGD(@"CNContactStore requesting authorization");
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
LOGD(@"CNContactStore authorization granted");
}];
} else if([CNContactStore authorizationStatusForEntityType:entityType]== CNAuthorizationStatusAuthorized) {
LOGD(@"CNContactStore authorization granted");
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateAddressBook:) name:CNContactStoreDidChangeNotification object:nil];
}
return self;
store = [[CNContactStore alloc] init];
_addressBookMap = [NSMutableDictionary dictionary];
[self reloadAllContacts];
}
self.needToUpdate = FALSE;
if (floor(NSFoundationVersionNumber) >=
NSFoundationVersionNumber_iOS_9_x_Max) {
if ([CNContactStore class]) {
// ios9 or later
if (store == NULL)
store = [[CNContactStore alloc] init];
CNEntityType entityType = CNEntityTypeContacts;
if ([CNContactStore authorizationStatusForEntityType:entityType] ==
CNAuthorizationStatusNotDetermined) {
LOGD(@"CNContactStore requesting authorization");
[store requestAccessForEntityType:entityType
completionHandler:^(BOOL granted,
NSError *_Nullable error) {
LOGD(@"CNContactStore authorization granted");
}];
} else if ([CNContactStore
authorizationStatusForEntityType:entityType] ==
CNAuthorizationStatusAuthorized) {
LOGD(@"CNContactStore authorization granted");
}
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(updateAddressBook:)
name:CNContactStoreDidChangeNotification
object:nil];
//[self reload];
store = [[CNContactStore alloc] init];
[self reloadAllContacts];
}
}
return self;
}
- (void)saveAddressBook {
if (addressBook != nil) {
if (!ABAddressBookSave(addressBook, nil)) {
LOGW(@"Couldn't save Address Book");
}
}
}
- (void)reload {
CFErrorRef error;
// create if it doesn't exist
if (addressBook == nil) {
addressBook = ABAddressBookCreateWithOptions(NULL, &error);
}
if (addressBook != nil) {
__weak FastAddressBook *weakSelf = self;
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (!granted) {
LOGE(@"Permission for address book acces was denied: %@", [(__bridge NSError *)error description]);
return;
}
ABAddressBookRegisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(weakSelf));
dispatch_async(dispatch_get_main_queue(), ^(void) {
[weakSelf loadData];
});
});
} else {
LOGE(@"Create AddressBook failed, reason: %@", [(__bridge NSError *)error localizedDescription]);
}
/*- (void)saveAddressBook {
if (addressBook != nil) {
if (!ABAddressBookSave(addressBook, nil)) {
LOGW(@"Couldn't save Address Book");
}
}
}
*/
-(void) updateAddressBook:(NSNotification*) notif {
LOGD(@"address book has changed");
self.needToUpdate = TRUE;
//[self reloadAllContacts];
}
/*- (void)reload {
[self getAllContacts];
LOGE(@"Create AddressBook failed");
}
*/
- (BOOL)reloadAllContacts {
BOOL success = FALSE;
if ([CNContactStore class]) {
[_addressBookMap removeAllObjects];
// iOS 9 or later
NSError *contactError;
[store
containersMatchingPredicate:[CNContainer
predicateForContainersWithIdentifiers:@[
store.defaultContainerIdentifier
]]
error:&contactError];
NSArray *keysToFetch = @[
CNContactEmailAddressesKey, CNContactPhoneNumbersKey,
CNContactFamilyNameKey, CNContactGivenNameKey,
CNContactPostalAddressesKey, CNContactIdentifierKey,
CNInstantMessageAddressUsernameKey, CNContactInstantMessageAddressesKey,
CNInstantMessageAddressUsernameKey, CNContactImageDataKey
];
CNContactFetchRequest *request =
[[CNContactFetchRequest alloc] initWithKeysToFetch:keysToFetch];
success =
[store enumerateContactsWithFetchRequest:request
error:&contactError
usingBlock:^(CNContact *__nonnull contact,
BOOL *__nonnull stop) {
if (contactError) {
NSLog(@"error fetching contacts %@",
contactError);
} else {
Contact *newContact = [[Contact alloc]
initWithCNContact:contact];
[_addressBookMap setObject:newContact
forKey:contact];
[self registerAddrsFor:newContact];
}
}];
// 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;
// only append friends that are not native contacts (already added
// above)
if (linphone_friend_get_ref_key(f) == NULL) {
Contact *contact = [[Contact alloc] initWithFriend:f];
[self registerAddrsFor:contact];
}
friends = friends->next;
}
linphone_friend_list_update_subscriptions(fl);
lists = lists->next;
}
}
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneAddressBookUpdate
object:self];
return success;
}
- (void)registerAddrsFor:(Contact *)contact {
for (NSString *phone in contact.phoneNumbers) {
char *normalizedPhone =
linphone_proxy_config_normalize_phone_number(linphone_core_get_default_proxy_config(LC), phone.UTF8String);
NSString *name =
[FastAddressBook normalizeSipURI:normalizedPhone ? [NSString stringWithUTF8String:normalizedPhone] : phone];
if (phone != NULL) {
[_addressBookMap setObject:contact forKey:(name ?: [FastAddressBook localizedLabel:phone])];
}
if (normalizedPhone)
ms_free(normalizedPhone);
}
for (NSString *sip in contact.sipAddresses) {
[_addressBookMap setObject:contact forKey:([FastAddressBook normalizeSipURI:sip] ?: 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);
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;
// only append friends that are not native contacts (already added above)
if (linphone_friend_get_ref_key(f) == NULL) {
Contact *contact = [[Contact alloc] initWithFriend:f];
[self registerAddrsFor:contact];
}
friends = friends->next;
}
linphone_friend_list_update_subscriptions(fl);
lists = lists->next;
}
}
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneAddressBookUpdate object:self];
for (NSString *phone in contact.phones) {
char *normalizedPhone = linphone_proxy_config_normalize_phone_number(
linphone_core_get_default_proxy_config(LC), phone.UTF8String);
NSString *name = [FastAddressBook
normalizeSipURI:normalizedPhone
? [NSString stringWithUTF8String:normalizedPhone]
: phone];
if (phone != NULL) {
[_addressBookMap
setObject:contact
forKey:(name ?: [FastAddressBook localizedLabel:phone])];
}
if (normalizedPhone)
ms_free(normalizedPhone);
}
for (NSString *sip in contact.sipAddresses) {
[_addressBookMap setObject:contact
forKey:([FastAddressBook normalizeSipURI:sip] ?: sip)];
}
}
void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void *context) {
FastAddressBook *fastAddressBook = (__bridge FastAddressBook *)context;
[fastAddressBook loadData];
}
- (void)dealloc {
ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(self));
CFRelease(addressBook);
[fastAddressBook reloadAllContacts];
}
#pragma mark - Tools
+ (NSString *)localizedLabel:(NSString *)label {
if (label != nil) {
return CFBridgingRelease(ABAddressBookCopyLocalizedLabel((__bridge CFStringRef)(label)));
}
return @"";
return [CNLabeledValue localizedStringForLabel:label];
}
return @"";
}
+ (BOOL)contactHasValidSipDomain:(Contact *)contact {
if (contact == nil)
return NO;
// Check if one of the contact' sip URI matches the expected SIP filter
NSString *domain = LinphoneManager.instance.contactFilter;
@ -317,60 +338,103 @@ void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void
return ret;
}
- (int)removeContact:(Contact *)contact {
// Remove contact from book
@synchronized(_addressBookMap) {
if (contact.person && ABRecordGetRecordID(contact.person) != kABRecordInvalidID) {
CFErrorRef error = NULL;
ABAddressBookRemoveRecord(addressBook, contact.person, (CFErrorRef *)&error);
if (error != NULL) {
LOGE(@"Remove contact %p: Fail(%@)", contact, [(__bridge NSError *)error localizedDescription]);
} else {
LOGI(@"Remove contact %p: Success!", contact);
}
contact = NULL;
// Save address book
error = NULL;
ABAddressBookSave(addressBook, (CFErrorRef *)&error);
- (BOOL)deleteContact:(Contact *)contact {
return [self deleteCNContact:contact.person];
}
// TODO: stop reloading the whole address book but just clear the removed entries!
[self loadData];
- (CNContact *)getCNContactFromContact:(Contact *)acontact {
NSArray *keysToFetch = @[
CNContactEmailAddressesKey, CNContactPhoneNumbersKey,
CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPostalAddressesKey,
CNContactIdentifierKey, CNContactInstantMessageAddressesKey,
CNInstantMessageAddressUsernameKey, CNContactImageDataKey
];
CNMutableContact *mCNContact =
[[store unifiedContactWithIdentifier:acontact.identifier
keysToFetch:keysToFetch
error:nil] mutableCopy];
return mCNContact;
}
if (error != NULL) {
LOGE(@"Save AddressBook: Fail(%@)", [(__bridge NSError *)error localizedDescription]);
} else {
LOGI(@"Save AddressBook: Success!");
}
return error ? -1 : 0;
}
return -2;
}
- (BOOL)deleteCNContact:(CNContact *)contact {
CNSaveRequest *saveRequest = [[CNSaveRequest alloc] init];
[saveRequest deleteContact:[contact mutableCopy]];
@try {
NSLog(@"Success %d", [store executeSaveRequest:saveRequest error:nil]);
[self reloadAllContacts];
} @catch (NSException *exception) {
NSLog(@"description = %@", [exception description]);
return FALSE;
}
[self reloadAllContacts];
return TRUE;
}
- (BOOL)deleteAllContacts {
NSArray *keys = @[ CNContactPhoneNumbersKey ];
NSString *containerId = store.defaultContainerIdentifier;
NSPredicate *predicate =
[CNContact predicateForContactsInContainerWithIdentifier:containerId];
NSError *error;
NSArray *cnContacts = [store unifiedContactsMatchingPredicate:predicate
keysToFetch:keys
error:&error];
if (error) {
NSLog(@"error fetching contacts %@", error);
return FALSE;
} else {
CNSaveRequest *saveRequest = [[CNSaveRequest alloc] init];
for (CNContact *contact in cnContacts) {
[saveRequest deleteContact:[contact mutableCopy]];
}
@try {
NSLog(@"Success %d", [store executeSaveRequest:saveRequest error:nil]);
} @catch (NSException *exception) {
NSLog(@"description = %@", [exception description]);
return FALSE;
}
NSLog(@"Deleted contacts %lu", cnContacts.count);
}
return TRUE;
}
- (BOOL)saveContact:(Contact *)contact {
@synchronized(_addressBookMap) {
CFErrorRef error = NULL;
if (ABRecordGetRecordID(contact.person) == kABRecordInvalidID) {
if (ABAddressBookAddRecord(addressBook, contact.person, (CFErrorRef *)&error)) {
LOGI(@"Add contact %p: Success!", contact.person);
} else {
LOGE(@"Add contact %p: Fail(%@)", contact.person, [(__bridge NSError *)error localizedDescription]);
return FALSE;
}
}
return [self saveCNContact:contact.person contact:contact];
}
// Save address book
error = NULL;
if (ABAddressBookSave(addressBook, &error)) {
LOGI(@"Save AddressBook: Success!");
} else {
LOGE(@"Save AddressBook: Fail(%@)", [(__bridge NSError *)error localizedDescription]);
return FALSE;
}
[self reload];
- (BOOL)saveCNContact:(CNContact *)cNContact contact:(Contact *)contact {
CNSaveRequest *saveRequest = [[CNSaveRequest alloc] init];
NSArray *keysToFetch = @[
CNContactEmailAddressesKey, CNContactPhoneNumbersKey,
CNContactInstantMessageAddressesKey, CNInstantMessageAddressUsernameKey,
CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPostalAddressesKey,
CNContactIdentifierKey, CNContactImageDataKey, CNContactNicknameKey
];
CNMutableContact *mCNContact =
[[store unifiedContactWithIdentifier:contact.identifier
keysToFetch:keysToFetch
error:nil] mutableCopy];
[mCNContact setGivenName:contact.firstName];
[mCNContact setFamilyName:contact.lastName];
[mCNContact setNickname:contact.displayName];
[mCNContact setPhoneNumbers:contact.person.phoneNumbers];
[mCNContact setEmailAddresses:contact.person.emailAddresses];
[mCNContact
setInstantMessageAddresses:contact.person.instantMessageAddresses];
[mCNContact setImageData:UIImageJPEGRepresentation(contact.avatar, 0.9f)];
return error == NULL;
}
[saveRequest updateContact:mCNContact];
NSError *saveError;
@try {
NSLog(@"Success %d",
[store executeSaveRequest:saveRequest error:&saveError]);
[_addressBookMap setObject:contact forKey:cNContact];
} @catch (NSException *exception) {
NSLog(@"=====>>>>> CNContact SaveRequest failed : description = %@",
[exception description]);
return FALSE;
}
return TRUE;
}
@end

View file

@ -150,11 +150,14 @@
linphone_core_set_file_transfer_server(lc, "https://www.linphone.org:444/lft.php");
// reload address book to prepend proxy config domain to contacts' phone number
[[[LinphoneManager instance] fastAddressBook] reload];
[[[LinphoneManager instance] fastAddressBook]
reloadAllContacts];
[self waitForRegistration];
[[LinphoneManager instance] lpConfigSetInt:NO forKey:@"animations_preference"];
}
[self waitForRegistration];
[[LinphoneManager instance]
lpConfigSetInt:NO
forKey:@"animations_preference"];
}
}
- (UITableView *)findTableView:(NSString *)table {

View file

@ -29,8 +29,6 @@
228697C411AC29B800E9E0CA /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 228697C311AC29B800E9E0CA /* CFNetwork.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
22968A5F12F875C600588287 /* UISpeakerButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 22968A5E12F875C600588287 /* UISpeakerButton.m */; };
22AA8B0113D83F6300B30535 /* UICamSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 22AA8B0013D83F6300B30535 /* UICamSwitch.m */; };
22B5EFA310CE50BD00777D97 /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22B5EFA210CE50BD00777D97 /* AddressBookUI.framework */; };
22B5F03510CE6B2F00777D97 /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22B5F03410CE6B2F00777D97 /* AddressBook.framework */; };
22C755601317E59C007BC101 /* UIBluetoothButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 22C7555F1317E59C007BC101 /* UIBluetoothButton.m */; };
22D1B68112A3E0BE001AE361 /* libresolv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 22D1B68012A3E0BE001AE361 /* libresolv.dylib */; };
22E0A822111C44E100B04932 /* AboutView.m in Sources */ = {isa = PBXBuildFile; fileRef = 22E0A81C111C44E100B04932 /* AboutView.m */; };
@ -41,6 +39,7 @@
244523BE1E8D3A6C0037A187 /* chat_unsecure.png in Resources */ = {isa = PBXBuildFile; fileRef = 244523BC1E8D3A6C0037A187 /* chat_unsecure.png */; };
24A3459E1D95797700881A5C /* UIShopTableCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24A3459D1D95797700881A5C /* UIShopTableCell.xib */; };
24A345A61D95798A00881A5C /* UIShopTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A345A51D95798A00881A5C /* UIShopTableCell.m */; };
24E1C7C01F9A235600D3F981 /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1C7B91F9A235500D3F981 /* Contacts.framework */; };
288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; };
340751971506459A00B89C47 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340751961506459A00B89C47 /* CoreTelephony.framework */; };
340751E7150F38FD00B89C47 /* UIVideoButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 340751E6150F38FD00B89C47 /* UIVideoButton.m */; };
@ -828,20 +827,6 @@
remoteGlobalIDString = FAB8A0141CAC546A00C6DFC1;
remoteInfo = KIFFrameworkConsumerTests;
};
8C90F57A1F94A621003B86C4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 630589F21B4E816900EFAE36 /* KIF.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8CD87D4C1EF5105800ACA260;
remoteInfo = LinphoneManager;
};
8C90F57C1F94A621003B86C4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 630589F21B4E816900EFAE36 /* KIF.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8CD87D541EF5105900ACA260;
remoteInfo = LinphoneManagerTests;
};
F08F119119C09C6B007D70C2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
@ -963,6 +948,7 @@
24A3459D1D95797700881A5C /* UIShopTableCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UIShopTableCell.xib; sourceTree = "<group>"; };
24A345A51D95798A00881A5C /* UIShopTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIShopTableCell.m; sourceTree = "<group>"; };
24A345A71D95799900881A5C /* UIShopTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIShopTableCell.h; sourceTree = "<group>"; };
24E1C7B91F9A235500D3F981 /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; };
288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
32CA4F630368D1EE00C91783 /* linphone_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = linphone_Prefix.pch; sourceTree = "<group>"; };
@ -1863,6 +1849,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
24E1C7C01F9A235600D3F981 /* Contacts.framework in Frameworks */,
8C5BCED61EB3859300A9AAEF /* mediastreamer_voip.framework in Frameworks */,
8C2595DF1DEDCC8E007A6424 /* CallKit.framework in Frameworks */,
8C5BCED81EB3859300A9AAEF /* mssilk.framework in Frameworks */,
@ -1878,8 +1865,6 @@
6334DDFA1BBAC97C00631900 /* libsqlite3.dylib in Frameworks */,
152F22361B15E889008C0621 /* libxml2.dylib in Frameworks */,
570742671D5A63DB004B9C84 /* StoreKit.framework in Frameworks */,
22B5F03510CE6B2F00777D97 /* AddressBook.framework in Frameworks */,
22B5EFA310CE50BD00777D97 /* AddressBookUI.framework in Frameworks */,
22405EEE1600B4E400B92522 /* AssetsLibrary.framework in Frameworks */,
2274402F106F335E006EC466 /* AudioToolbox.framework in Frameworks */,
224567C2107B968500F10948 /* AVFoundation.framework in Frameworks */,
@ -2225,6 +2210,7 @@
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
24E1C7B91F9A235500D3F981 /* Contacts.framework */,
8C3EAA191EB8D9C300B732B6 /* linphonetester.framework */,
8C5BCEC61EB3859200A9AAEF /* bctoolbox-tester.framework */,
8C3EA9EF1EB8A78C00B732B6 /* msx264.framework */,
@ -2316,8 +2302,6 @@
63058A071B4E816A00EFAE36 /* KIF.framework */,
633FC7C81CD7466400774B8B /* KIFFrameworkConsumer.app */,
633FC7CA1CD7466400774B8B /* KIFFrameworkConsumerTests.xctest */,
8C90F57B1F94A621003B86C4 /* LinphoneManager.framework */,
8C90F57D1F94A621003B86C4 /* LinphoneManagerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -3242,20 +3226,6 @@
remoteRef = 633FC7C91CD7466400774B8B /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8CA26B501F7D31E700411264 /* LinphoneManager.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = LinphoneManager.framework;
remoteRef = 8CA26B4F1F7D31E700411264 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8CA26B521F7D31E700411264 /* LinphoneManagerTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = LinphoneManagerTests.xctest;
remoteRef = 8CA26B511F7D31E700411264 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */