Update dynamically adress book, linphoneImahe on valid sip address or phone number in contact detail

This commit is contained in:
REIS Benjamin 2016-10-13 11:51:06 +02:00
parent bd2ad5e17b
commit 76da97acfc
9 changed files with 141 additions and 29 deletions

View file

@ -113,6 +113,15 @@ static int ms_strcmpfuz(const char *fuzzy_word, const char *sentence) {
- (void)loadData {
LOGI(@"Load contact list");
@synchronized(addressBookMap) {
//Set all contacts from ContactCell to nil
for (NSInteger j = 0; j < [self.tableView numberOfSections]; ++j)
{
for (NSInteger i = 0; i < [self.tableView numberOfRowsInSection:j]; ++i)
{
[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]] setContact:nil];
}
}
// Reset Address book
[addressBookMap removeAllObjects];

View file

@ -18,6 +18,7 @@
*/
#import "PhoneMainView.h"
#import "ContactsListView.h"
#import "ShopView.h"
#import "linphoneAppDelegate.h"
#import "AddressBook/ABPerson.h"
@ -87,6 +88,20 @@
LinphoneManager *instance = LinphoneManager.instance;
[instance becomeActive];
if (instance.fastAddressBook.needToUpdate) {
//Update address book for external changes
if (PhoneMainView.instance.currentView == ContactsListView.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;
}
}
LinphoneCall *call = linphone_core_get_current_call(LC);

View file

@ -43,6 +43,8 @@
#import <AVFoundation/AVAudioPlayer.h>
#import "Utils.h"
#import "PhoneMainView.h"
#import "ChatsListView.h"
#import "ChatConversationView.h"
#import <UserNotifications/UserNotifications.h>
#define LINPHONE_LOGS_MAX_ENTRY 5000
@ -1217,7 +1219,6 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, const char
}];
}
}
// Post event
NSDictionary *dict = @{
@"room" : [NSValue valueWithPointer:room],

View file

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="UIContactDetailsCell">
@ -14,6 +15,7 @@
<outlet property="deleteButton" destination="C2f-aP-xjR" id="sxr-th-6rq"/>
<outlet property="editTextfield" destination="dTn-Hc-bGM" id="bkN-xg-S9D"/>
<outlet property="editView" destination="rAa-qu-PDc" id="cGz-D2-GiI"/>
<outlet property="linphoneImage" destination="ZaI-29-AOK" id="dY1-vO-spk"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
@ -22,23 +24,23 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" id="rAa-qu-PDc" userLabel="editView">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<frame key="frameInset" minX="0.0%" minY="0.0%" width="100.00%" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" minimumFontSize="17" id="dTn-Hc-bGM" userLabel="editTextField">
<rect key="frame" x="8" y="7" width="327" height="30"/>
<frame key="frameInset" minX="8" minY="15.91%" height="68.18%" maxX="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="0.90588235289999997" green="0.90588235289999997" blue="0.90588235289999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" red="0.88286077976226807" green="0.88283431529998779" blue="0.88284933567047119" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<rect key="contentStretch" x="1.3877787807814457e-17" y="0.0" width="1" height="1"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" returnKeyType="done"/>
</textField>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="C2f-aP-xjR" userLabel="deleteButton" customClass="UIIconButton">
<rect key="frame" x="335" y="7" width="40" height="30"/>
<frame key="frameInset" minY="50.00%" width="40" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<inset key="contentEdgeInsets" minX="5" minY="0.0" maxX="5" maxY="0.0"/>
<state key="normal" image="delete_field_default.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<state key="selected" image="delete_field_over.png"/>
<state key="highlighted" image="delete_field_over.png"/>
@ -47,18 +49,18 @@
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<view contentMode="scaleToFill" id="SR2-3m-6t5" userLabel="defaultView">
<rect key="frame" x="0.0" y="0.0" width="375" height="88"/>
<frame key="frameInset" minX="0.0%" minY="0.0%" width="100.00%" height="100.00%"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="ZbV-2Z-b4y" userLabel="callButton" customClass="UIIconButton">
<rect key="frame" x="135" y="40" width="44" height="44"/>
<frame key="frameInset" minX="40.79%" width="44" height="44" maxY="4"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Call"/>
<state key="normal" image="call_start_body_default.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<state key="disabled" image="call_start_body_disabled.png"/>
<state key="highlighted" image="call_start_body_over.png"/>
@ -67,11 +69,11 @@
</connections>
</button>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="FDT-HY-OQZ" userLabel="chatButton" customClass="UIIconButton">
<rect key="frame" x="195" y="40" width="44" height="44"/>
<frame key="frameInset" minX="58.91%" width="44" height="44" maxY="4"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Chat"/>
<state key="normal" image="chat_start_body_default.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<state key="disabled" image="chat_start_body_disabled.png"/>
<state key="highlighted" image="chat_start_body_over.png"/>
@ -80,16 +82,20 @@
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="john.doe@sip.linphone.org" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="frB-ep-LWi" userLabel="addressLabel">
<rect key="frame" x="8" y="0.0" width="359" height="44"/>
<frame key="frameInset" minX="2.13%" width="95.73%" height="50.00%"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="linphone_user.png" id="ZaI-29-AOK" userLabel="linphoneImage">
<frame key="frameInset" minY="77.27%" width="25" height="22" maxX="8"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="338.5" y="43"/>
@ -104,5 +110,11 @@
<image name="chat_start_body_over.png" width="51" height="51"/>
<image name="delete_field_default.png" width="27" height="27"/>
<image name="delete_field_over.png" width="27" height="27"/>
<image name="linphone_user.png" width="26" height="22"/>
</resources>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/>
</simulatedMetricsContainer>
</document>

View file

@ -61,8 +61,10 @@
- (void)onPresenceChanged:(NSNotification *)k {
LinphoneFriend *f = [[k.userInfo valueForKey:@"friend"] pointerValue];
// only consider event if it's about us
if (!_contact.friend || f != _contact.friend) {
return;
if (_contact) {
if (!_contact.friend || f != _contact.friend) {
return;
}
}
[self setContact:_contact];
}
@ -71,9 +73,10 @@
- (void)setContact:(Contact *)acontact {
_contact = acontact;
[ContactDisplay setDisplayNameLabel:_nameLabel forContact:_contact];
_linphoneImage.hidden = ! (_contact.friend && linphone_presence_model_get_basic_status(linphone_friend_get_presence_model(_contact.friend)) == LinphonePresenceBasicStatusOpen);
if(_contact) {
[ContactDisplay setDisplayNameLabel:_nameLabel forContact:_contact];
_linphoneImage.hidden = ! (_contact.friend && linphone_presence_model_get_basic_status(linphone_friend_get_presence_model(_contact.friend)) == LinphonePresenceBasicStatusOpen);
}
}
#pragma mark -

View file

@ -34,6 +34,7 @@
@property(weak, nonatomic) IBOutlet UIIconButton *deleteButton;
@property(weak, nonatomic) IBOutlet UIIconButton *callButton;
@property(weak, nonatomic) IBOutlet UIIconButton *chatButton;
@property (weak, nonatomic) IBOutlet UIImageView *linphoneImage;
- (id)initWithIdentifier:(NSString *)identifier;
- (void)setAddress:(NSString *)address;

View file

@ -18,6 +18,8 @@
*/
#import "UIContactDetailsCell.h"
#import "FastAddressBook.h"
#import "Contact.h"
#import "PhoneMainView.h"
@implementation UIContactDetailsCell
@ -48,6 +50,12 @@
_chatButton.accessibilityLabel =
[NSString stringWithFormat:NSLocalizedString(@"Chat with %@", nil), _addressLabel.text];
_callButton.accessibilityLabel = [NSString stringWithFormat:NSLocalizedString(@"Call %@", nil), _addressLabel.text];
// Test presence
Contact* contact = [FastAddressBook getContactWithAddress:(addr)];
if (contact) {
self.linphoneImage.hidden = ! ((contact.friend && linphone_presence_model_get_basic_status(linphone_friend_get_presence_model_for_uri_or_tel(contact.friend, _addressLabel.text.UTF8String)) == LinphonePresenceBasicStatusOpen) || (!linphone_proxy_config_is_phone_number(linphone_core_get_default_proxy_config(LC), _addressLabel.text.UTF8String) && [FastAddressBook isSipURIValid:_addressLabel.text]));
}
if (addr) {
linphone_address_destroy(addr);
}

View file

@ -26,6 +26,7 @@
@interface FastAddressBook : NSObject
@property(readonly, nonatomic) NSMutableDictionary *addressBookMap;
@property BOOL needToUpdate;
- (void)reload;
- (void)saveAddressBook;
@ -42,6 +43,7 @@
+ (UIImage *)imageForAddress:(const LinphoneAddress *)addr thumbnail:(BOOL)thumbnail;
+ (BOOL)contactHasValidSipDomain:(Contact *)person;
+ (BOOL)isSipURIValid:(NSString*)addr;
+ (NSString *)displayNameForContact:(Contact *)person;
+ (NSString *)displayNameForAddress:(const LinphoneAddress *)addr;

View file

@ -16,7 +16,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef __IPHONE_9_0
#import <Contacts/Contacts.h>
#endif
#import "FastAddressBook.h"
#import "LinphoneManager.h"
#import "ContactsListView.h"
@ -24,6 +26,7 @@
@implementation FastAddressBook {
ABAddressBookRef addressBook;
CNContactStore* store;
}
static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void *context);
@ -95,6 +98,39 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info
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];
[contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
if(granted){
NSError* contactError;
store = [[CNContactStore alloc]init];
[store containersMatchingPredicate:[CNContainer predicateForContainersWithIdentifiers: @[store.defaultContainerIdentifier]] error:&contactError];
NSArray * keysToFetch =@[CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPostalAddressesKey];
CNContactFetchRequest * request = [[CNContactFetchRequest alloc]initWithKeysToFetch:keysToFetch];
BOOL success = [store enumerateContactsWithFetchRequest:request error:&contactError usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop){}];
if(success) {
LOGD(@"CNContactStore successfully synchronized");
}
}
}];
} else if([CNContactStore authorizationStatusForEntityType:entityType]== CNAuthorizationStatusAuthorized) {
NSError* contactError;
store = [[CNContactStore alloc]init];
[store containersMatchingPredicate:[CNContainer predicateForContainersWithIdentifiers: @[store.defaultContainerIdentifier]] error:&contactError];
NSArray * keysToFetch =@[CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPostalAddressesKey];
CNContactFetchRequest * request = [[CNContactFetchRequest alloc]initWithKeysToFetch:keysToFetch];
BOOL success = [store enumerateContactsWithFetchRequest:request error:&contactError usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop){}];
if(success) {
LOGD(@"CNContactStore successfully synchronized");
}
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateAddressBook:) name:CNContactStoreDidChangeNotification object:nil];
}
return self;
}
@ -117,13 +153,13 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info
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;
}
if (!granted) {
LOGE(@"Permission for address book acces was denied: %@", [(__bridge NSError *)error description]);
return;
}
ABAddressBookRegisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(weakSelf));
[weakSelf loadData];
ABAddressBookRegisterExternalChangeCallback(addressBook, sync_address_book, (__bridge void *)(weakSelf));
[weakSelf loadData];
});
} else {
@ -131,6 +167,11 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info
}
}
-(void) updateAddressBook:(NSNotification*) notif {
LOGD(@"address book has changed");
self.needToUpdate = TRUE;
}
- (void)registerAddrsFor:(Contact *)contact {
for (NSString *phone in contact.phoneNumbers) {
char *normalizedPhone =
@ -177,6 +218,7 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info
}
friends = friends->next;
}
linphone_friend_list_update_subscriptions(fl);
lists = lists->next;
}
}
@ -228,6 +270,25 @@ void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info, void
return NO;
}
+ (BOOL) isSipURIValid:(NSString*)addr {
NSString *domain = LinphoneManager.instance.contactFilter;
LinphoneAddress* address = linphone_core_interpret_url(LC, addr.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;
}
}
return NO;
}
+ (NSString *)displayNameForContact:(Contact *)contact {
return contact.displayName;
}