forked from mirrors/linphone-iphone
526 lines
16 KiB
Objective-C
526 lines
16 KiB
Objective-C
//
|
|
// Contact.m
|
|
// linphone
|
|
//
|
|
// Created by Gautier Pelloux-Prayer on 12/01/16.
|
|
//
|
|
//
|
|
|
|
#import "Contact.h"
|
|
|
|
@implementation Contact
|
|
|
|
- (instancetype)initWithPerson:(ABRecordRef)aperson {
|
|
return [self initWithPerson:aperson andFriend:NULL];
|
|
}
|
|
|
|
- (instancetype)initWithFriend:(LinphoneFriend *)afriend {
|
|
return [self initWithPerson:NULL andFriend:afriend];
|
|
}
|
|
|
|
- (instancetype)initWithPerson:(ABRecordRef)aperson andFriend:(LinphoneFriend *)afriend {
|
|
self = [super init];
|
|
_person = aperson;
|
|
_friend = afriend ? linphone_friend_ref(afriend) : NULL;
|
|
|
|
if (_person) {
|
|
[self loadProperties];
|
|
|
|
const char* key = [NSString stringWithFormat:@"ab%d", ABRecordGetRecordID(aperson)].UTF8String;
|
|
// try to find friend associated with that person
|
|
_friend = linphone_friend_list_find_friend_by_ref_key(linphone_core_get_default_friend_list(LC), key);
|
|
if (!_friend) {
|
|
_friend = linphone_friend_ref(linphone_core_create_friend(LC));
|
|
linphone_friend_set_ref_key(_friend, key);
|
|
linphone_friend_set_name(_friend, [NSString stringWithFormat:@"%@ %@", _firstName, _lastName].UTF8String);
|
|
for (NSString* sipAddr in _sipAddresses) {
|
|
LinphoneAddress* addr = linphone_core_interpret_url(LC, sipAddr.UTF8String);
|
|
if (addr) {
|
|
linphone_address_set_display_name(addr, [self displayName].UTF8String);
|
|
linphone_friend_add_address(_friend, addr);
|
|
linphone_address_destroy(addr);
|
|
}
|
|
}
|
|
for (NSString* phone in _phoneNumbers) {
|
|
#if 0
|
|
char* normalized_phone = linphone_proxy_config_normalize_phone_number(linphone_core_get_default_proxy_config(LC), phone.UTF8String);
|
|
if (normalized_phone) {
|
|
LinphoneAddress* addr = linphone_core_interpret_url(LC, normalized_phone);
|
|
if (addr) {
|
|
linphone_address_set_display_name(addr, [self displayName].UTF8String);
|
|
linphone_friend_add_address(_friend, addr);
|
|
linphone_address_destroy(addr);
|
|
}
|
|
ms_free(normalized_phone);
|
|
}
|
|
#else
|
|
linphone_friend_add_phone_number(_friend, phone.UTF8String);
|
|
#endif
|
|
}
|
|
if (_friend) {
|
|
linphone_friend_enable_subscribes(_friend, FALSE);
|
|
linphone_friend_set_inc_subscribe_policy(_friend, LinphoneSPDeny);
|
|
linphone_core_add_friend(LC, _friend);
|
|
}
|
|
}
|
|
linphone_friend_ref(_friend);
|
|
} else if (_friend) {
|
|
[self loadFriend];
|
|
} else {
|
|
LOGE(@"Contact cannot be initialized");
|
|
return nil;
|
|
}
|
|
|
|
LOGI(@"Contact %@ %@ initialized with %d phones, %d sip, %d emails", self.firstName ?: @"", self.lastName ?: @"",
|
|
self.phoneNumbers.count, self.sipAddresses.count, self.emails.count);
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
if (_person != nil && ABRecordGetRecordID(_person) == kABRecordInvalidID) {
|
|
CFRelease(_person);
|
|
}
|
|
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 (_friend) {
|
|
// const char *dp = linphone_address_get_display_name(linphone_friend_get_address(_friend));
|
|
//if (dp)
|
|
// return [NSString stringWithUTF8String:dp];
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (_lastName || _firstName) {
|
|
NSMutableString *str;
|
|
if (_firstName)
|
|
[str appendString:_firstName];
|
|
if (_firstName && _lastName)
|
|
[str appendString:@" "];
|
|
if (_lastName)
|
|
[str appendString:_lastName];
|
|
}
|
|
|
|
return NSLocalizedString(@"Unknown", nil);
|
|
}
|
|
|
|
#pragma mark - Setters
|
|
|
|
- (void)setAvatar:(UIImage *)avatar {
|
|
if (_person) {
|
|
CFErrorRef error = NULL;
|
|
if (!ABPersonRemoveImageData(_person, &error)) {
|
|
LOGW(@"Can't remove entry: %@", [(__bridge NSError *)error localizedDescription]);
|
|
}
|
|
NSData *dataRef = UIImageJPEGRepresentation(avatar, 0.9f);
|
|
CFDataRef cfdata = CFDataCreate(NULL, [dataRef bytes], [dataRef length]);
|
|
|
|
if (!ABPersonSetImageData(_person, cfdata, &error)) {
|
|
LOGW(@"Can't add entry: %@", [(__bridge NSError *)error localizedDescription]);
|
|
}
|
|
|
|
CFRelease(cfdata);
|
|
} else {
|
|
LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__);
|
|
}
|
|
}
|
|
|
|
- (void)setFirstName:(NSString *)firstName {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
ret = ([self replaceInProperty:kABPersonFirstNameProperty value:(__bridge CFTypeRef)(firstName)]);
|
|
} else {
|
|
ret = (linphone_friend_set_name(_friend, firstName.UTF8String) == 0);
|
|
}
|
|
|
|
if (ret) {
|
|
_firstName = firstName;
|
|
}
|
|
}
|
|
|
|
- (void)setLastName:(NSString *)lastName {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
ret = ([self replaceInProperty:kABPersonLastNameProperty value:(__bridge CFTypeRef)(lastName)]);
|
|
} else {
|
|
LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__);
|
|
}
|
|
|
|
if (ret) {
|
|
_lastName = lastName;
|
|
}
|
|
}
|
|
|
|
- (BOOL)setSipAddress:(NSString *)sip atIndex:(NSInteger)index {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
NSDictionary *lDict = @{
|
|
(NSString *) kABPersonInstantMessageUsernameKey : sip, (NSString *)
|
|
kABPersonInstantMessageServiceKey : LinphoneManager.instance.contactSipField
|
|
};
|
|
|
|
ret = [self replaceInProperty:kABPersonInstantMessageProperty value:(__bridge CFTypeRef)(lDict) atIndex:index];
|
|
} else {
|
|
LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__);
|
|
}
|
|
|
|
if (ret) {
|
|
_sipAddresses[index] = sip;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)setPhoneNumber:(NSString *)phone atIndex:(NSInteger)index {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
ret = [self replaceInProperty:kABPersonPhoneProperty value:(__bridge CFTypeRef)(phone) atIndex:index];
|
|
} else {
|
|
LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__);
|
|
}
|
|
if (ret) {
|
|
_phoneNumbers[index] = phone;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)setEmail:(NSString *)email atIndex:(NSInteger)index {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
ret = [self replaceInProperty:kABPersonEmailProperty value:(__bridge CFTypeRef)(email) atIndex:index];
|
|
} else {
|
|
}
|
|
if (ret) {
|
|
_emails[index] = email;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)addSipAddress:(NSString *)sip {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
NSDictionary *lDict = @{
|
|
(NSString *) kABPersonInstantMessageUsernameKey : sip, (NSString *)
|
|
kABPersonInstantMessageServiceKey : LinphoneManager.instance.contactSipField
|
|
};
|
|
|
|
ret = [self addInProperty:kABPersonInstantMessageProperty value:(__bridge CFTypeRef)(lDict)];
|
|
} else {
|
|
LinphoneAddress *addr = linphone_core_interpret_url(LC, sip.UTF8String) ?: linphone_address_new(sip.UTF8String);
|
|
if (addr) {
|
|
ret = TRUE;
|
|
linphone_friend_add_address(_friend, addr);
|
|
linphone_address_destroy(addr);
|
|
// ensure that it was added by checking list size
|
|
ret = (bctbx_list_size(linphone_friend_get_addresses(_friend)) == _sipAddresses.count + 1);
|
|
}
|
|
}
|
|
if (ret) {
|
|
[_sipAddresses addObject:sip];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)addPhoneNumber:(NSString *)phone {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
ret = [self addInProperty:kABPersonPhoneProperty value:(__bridge CFTypeRef)(phone)];
|
|
} else {
|
|
char *cphone =
|
|
linphone_proxy_config_normalize_phone_number(NULL, phone.UTF8String) ?: ms_strdup(phone.UTF8String);
|
|
if (cphone) {
|
|
linphone_friend_add_phone_number(_friend, cphone);
|
|
phone = [NSString stringWithUTF8String:cphone];
|
|
ms_free(cphone);
|
|
// ensure that it was added by checking list size
|
|
ret = (bctbx_list_size(linphone_friend_get_phone_numbers(_friend)) == _phoneNumbers.count + 1);
|
|
}
|
|
}
|
|
if (ret) {
|
|
[_phoneNumbers addObject:phone];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)addEmail:(NSString *)email {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
ret = [self addInProperty:kABPersonEmailProperty value:(__bridge CFTypeRef)(email)];
|
|
} else {
|
|
LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__);
|
|
}
|
|
if (ret) {
|
|
[_emails addObject:email];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)removeSipAddressAtIndex:(NSInteger)index {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
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_address_destroy(addr);
|
|
// ensure that it was destroyed by checking list size
|
|
ret = (bctbx_list_size(linphone_friend_get_addresses(_friend)) + 1 == _sipAddresses.count);
|
|
}
|
|
}
|
|
if (ret) {
|
|
[_sipAddresses removeObjectAtIndex:index];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)removePhoneNumberAtIndex:(NSInteger)index {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
ret = [self removeInProperty:kABPersonPhoneProperty atIndex:index];
|
|
} else {
|
|
const char *phone = ((NSString *)_phoneNumbers[index]).UTF8String;
|
|
linphone_friend_remove_phone_number(_friend, phone);
|
|
// ensure that it was destroyed by checking list size
|
|
ret = (bctbx_list_size(linphone_friend_get_phone_numbers(_friend)) + 1 == _phoneNumbers.count);
|
|
}
|
|
if (ret) {
|
|
[_phoneNumbers removeObjectAtIndex:index];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)removeEmailAtIndex:(NSInteger)index {
|
|
BOOL ret = FALSE;
|
|
if (_person) {
|
|
ret = [self removeInProperty:kABPersonEmailProperty atIndex:index];
|
|
} else {
|
|
LOGW(@"%s: Cannot do it when using LinphoneFriend, skipping", __FUNCTION__);
|
|
}
|
|
if (ret) {
|
|
[_emails removeObjectAtIndex:index];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#pragma mark - ABPerson utils
|
|
|
|
- (void)loadProperties {
|
|
// First and Last name
|
|
{
|
|
_firstName = (NSString *)CFBridgingRelease(ABRecordCopyValue(_person, kABPersonFirstNameProperty));
|
|
_lastName = (NSString *)CFBridgingRelease(ABRecordCopyValue(_person, kABPersonLastNameProperty));
|
|
}
|
|
|
|
// Phone numbers
|
|
{
|
|
_phoneNumbers = [[NSMutableArray alloc] init];
|
|
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);
|
|
if (normalizedPhone) {
|
|
valueRef = [NSString stringWithUTF8String:normalizedPhone];
|
|
ms_free(normalizedPhone);
|
|
}
|
|
|
|
[_phoneNumbers addObject:valueRef];
|
|
}
|
|
}
|
|
CFRelease(map);
|
|
}
|
|
}
|
|
|
|
// SIP (IM)
|
|
{
|
|
_sipAddresses = [[NSMutableArray alloc] init];
|
|
ABMultiValueRef map = ABRecordCopyValue(_person, kABPersonInstantMessageProperty);
|
|
if (map) {
|
|
for (int i = 0; i < ABMultiValueGetCount(map); ++i) {
|
|
CFDictionaryRef lDict = ABMultiValueCopyValueAtIndex(map, i);
|
|
if (CFDictionaryContainsKey(lDict, kABPersonInstantMessageServiceKey)) {
|
|
if (CFStringCompare((CFStringRef)LinphoneManager.instance.contactSipField,
|
|
CFDictionaryGetValue(lDict, kABPersonInstantMessageServiceKey),
|
|
kCFCompareCaseInsensitive) == 0) {
|
|
NSString *value = (NSString *)(CFDictionaryGetValue(lDict, kABPersonInstantMessageUsernameKey));
|
|
CFRelease(lDict);
|
|
if (value != NULL) {
|
|
[_sipAddresses addObject:value];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CFRelease(map);
|
|
}
|
|
}
|
|
|
|
// Email
|
|
{
|
|
_emails = [[NSMutableArray alloc] init];
|
|
ABMultiValueRef map = ABRecordCopyValue(_person, kABPersonEmailProperty);
|
|
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));
|
|
if (valueRef != NULL) {
|
|
[_emails addObject:valueRef];
|
|
}
|
|
}
|
|
}
|
|
CFRelease(map);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (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);
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)replaceInProperty:(ABPropertyID)property value:(CFTypeRef)value atIndex:(NSInteger)index {
|
|
ABMultiValueRef lcMap = ABRecordCopyValue(_person, property);
|
|
ABMutableMultiValueRef lMap;
|
|
if (lcMap != NULL) {
|
|
lMap = ABMultiValueCreateMutableCopy(lcMap);
|
|
CFRelease(lcMap);
|
|
} else {
|
|
lMap = ABMultiValueCreateMutable(kABStringPropertyType);
|
|
}
|
|
|
|
BOOL ret = ABMultiValueReplaceValueAtIndex(lMap, value, index);
|
|
if (ret) {
|
|
ret = [self replaceInProperty:property value:lMap];
|
|
} else {
|
|
LOGW(@"Could not replace %@ at index %d from property %d", value, index, property);
|
|
}
|
|
|
|
CFRelease(lMap);
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)addInProperty:(ABPropertyID)property value:(CFTypeRef)value {
|
|
ABMultiValueRef lcMap = ABRecordCopyValue(_person, property);
|
|
ABMutableMultiValueRef lMap;
|
|
if (lcMap != NULL) {
|
|
lMap = ABMultiValueCreateMutableCopy(lcMap);
|
|
CFRelease(lcMap);
|
|
} else {
|
|
lMap = ABMultiValueCreateMutable(kABStringPropertyType);
|
|
}
|
|
|
|
// will display this field with our application name
|
|
CFStringRef label = (__bridge CFStringRef)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
|
BOOL ret = ABMultiValueAddValueAndLabel(lMap, value, label, nil);
|
|
if (ret) {
|
|
ret = [self replaceInProperty:property value:lMap];
|
|
} else {
|
|
LOGW(@"Could not add %@ to property %d", value, property);
|
|
}
|
|
CFRelease(lMap);
|
|
return ret;
|
|
}
|
|
|
|
- (BOOL)removeInProperty:(ABPropertyID)property atIndex:(NSInteger)index {
|
|
ABMultiValueRef lcMap = ABRecordCopyValue(_person, property);
|
|
ABMutableMultiValueRef lMap;
|
|
if (lcMap != NULL) {
|
|
lMap = ABMultiValueCreateMutableCopy(lcMap);
|
|
CFRelease(lcMap);
|
|
} else {
|
|
lMap = ABMultiValueCreateMutable(kABStringPropertyType);
|
|
}
|
|
|
|
BOOL ret = ABMultiValueRemoveValueAndLabelAtIndex(lMap, index);
|
|
if (ret) {
|
|
ret = [self replaceInProperty:property value:lMap];
|
|
} else {
|
|
LOGW(@"Could not remove at index %d from property %d", index, property);
|
|
}
|
|
|
|
CFRelease(lMap);
|
|
return ret;
|
|
}
|
|
|
|
#pragma mark - LinphoneFriend utils
|
|
|
|
- (void)loadFriend {
|
|
// First and Last name
|
|
{
|
|
_firstName = [NSString stringWithUTF8String:linphone_friend_get_name(_friend) ?: ""];
|
|
_lastName = nil;
|
|
}
|
|
|
|
// Phone numbers
|
|
{
|
|
_phoneNumbers = [[NSMutableArray alloc] init];
|
|
MSList *numbers = linphone_friend_get_phone_numbers(_friend);
|
|
while (numbers) {
|
|
NSString *phone = [NSString stringWithUTF8String:numbers->data];
|
|
[_phoneNumbers addObject:phone];
|
|
numbers = numbers->next;
|
|
}
|
|
}
|
|
|
|
// SIP (IM)
|
|
{
|
|
_sipAddresses = [[NSMutableArray alloc] init];
|
|
const MSList *sips = linphone_friend_get_addresses(_friend);
|
|
while (sips) {
|
|
LinphoneAddress *addr = sips->data;
|
|
char *uri = linphone_address_as_string_uri_only(addr);
|
|
NSString *sipaddr = [NSString stringWithUTF8String:uri];
|
|
[_sipAddresses addObject:sipaddr];
|
|
ms_free(uri);
|
|
|
|
sips = sips->next;
|
|
}
|
|
}
|
|
|
|
// Email - no support for LinphoneFriend
|
|
{ _emails = [[NSMutableArray alloc] init]; }
|
|
}
|
|
|
|
@end
|