@ -27,6 +27,7 @@ typedef BOOL (^ContactSearchBlock)(id, NSUInteger, BOOL*);
}
}
return self ;
return self ;
}
}
- ( void ) doAfterEnvironmentInitSetup {
- ( void ) doAfterEnvironmentInitSetup {
[ self setupAddressBook ] ;
[ self setupAddressBook ] ;
[ observableContactsController watchLatestValueOnArbitraryThread : ^( NSArray * latestContacts ) {
[ observableContactsController watchLatestValueOnArbitraryThread : ^( NSArray * latestContacts ) {
@ -46,6 +47,12 @@ typedef BOOL (^ContactSearchBlock)(id, NSUInteger, BOOL*);
[ life cancel ] ;
[ life cancel ] ;
}
}
- ( void ) verifyABPermission {
if ( !addressBookReference ) {
[ self setupAddressBook ] ;
}
}
#pragma mark - Notification Handlers
#pragma mark - Notification Handlers
- ( void ) registerNotificationHandlers {
- ( void ) registerNotificationHandlers {
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( updatedDirectoryHandler : ) name : NOTIFICATION_DIRECTORY_UPDATE object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( updatedDirectoryHandler : ) name : NOTIFICATION_DIRECTORY_UPDATE object : nil ] ;
@ -55,7 +62,7 @@ typedef BOOL (^ContactSearchBlock)(id, NSUInteger, BOOL*);
NSArray * currentUsers = [ self getSignalUsersFromContactsArray : latestContactsById . allValues ] ;
NSArray * currentUsers = [ self getSignalUsersFromContactsArray : latestContactsById . allValues ] ;
[ observableRedPhoneUsersController updateValue : currentUsers ] ;
[ observableRedPhoneUsersController updateValue : currentUsers ] ;
}
}
#pragma mark - Address Book callbacks
#pragma mark - Address Book callbacks
@ -88,7 +95,9 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions ( NULL , & creationError ) ;
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions ( NULL , & creationError ) ;
checkOperationDescribe ( nil == creationError , [ ( ( __bridge NSError * ) creationError ) localizedDescription ] ) ;
checkOperationDescribe ( nil == creationError , [ ( ( __bridge NSError * ) creationError ) localizedDescription ] ) ;
ABAddressBookRequestAccessWithCompletion ( addressBookRef , ^( bool granted , CFErrorRef error ) {
ABAddressBookRequestAccessWithCompletion ( addressBookRef , ^( bool granted , CFErrorRef error ) {
/ / TO DO : DISPLAY ALERT
if ( !granted ) {
[ ContactsManager blockingContactDialog ] ;
}
} ) ;
} ) ;
[ observableContactsController updateValue : [ self getContactsFromAddressBook : addressBookRef ] ] ;
[ observableContactsController updateValue : [ self getContactsFromAddressBook : addressBookRef ] ] ;
}
}
@ -103,6 +112,20 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
}
}
}
}
+ ( void ) blockingContactDialog {
UIAlertController * controller = [ UIAlertController alertControllerWithTitle : NSLocalizedString ( @ "AB_PERMISSION_MISSING_TITLE ", nil )
message : NSLocalizedString ( @ "AB_PERMISSION_MISSING_BODY ", nil )
preferredStyle : UIAlertControllerStyleAlert ] ;
[ controller addAction : [ UIAlertAction actionWithTitle : NSLocalizedString ( @ "AB_PERMISSION_MISSING_ACTION ", nil )
style : UIAlertActionStyleDefault
handler : ^( UIAlertAction * action ) {
[ [ UIApplication sharedApplication ] openURL : [ NSURL URLWithString : UIApplicationOpenSettingsURLString ] ] ;
} ] ] ;
[ [ [ UIApplication sharedApplication ] keyWindow ] . rootViewController presentViewController : controller animated : YES completion : nil ] ;
}
- ( void ) setupLatestRedPhoneUsers : ( NSArray * ) users {
- ( void ) setupLatestRedPhoneUsers : ( NSArray * ) users {
if ( users ) {
if ( users ) {
latestWhisperUsersById = [ ContactsManager keyContactsById : users ] ;
latestWhisperUsersById = [ ContactsManager keyContactsById : users ] ;
@ -126,11 +149,12 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions ( NULL , & creationError ) ;
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions ( NULL , & creationError ) ;
assert ( ( addressBookRef == nil ) == ( creationError != nil ) ) ;
assert ( ( addressBookRef == nil ) == ( creationError != nil ) ) ;
if ( creationError != nil ) {
if ( creationError != nil ) {
[ self blockingContactDialog ] ;
return [ TOCFuture futureWithFailure : ( __bridge_transfer id ) creationError ] ;
return [ TOCFuture futureWithFailure : ( __bridge_transfer id ) creationError ] ;
}
}
TOCFutureSource * futureAddressBookSource = [ TOCFutureSource new ] ;
TOCFutureSource * futureAddressBookSource = [ TOCFutureSource new ] ;
id addressBook = ( __bridge_transfer id ) addressBookRef ;
id addressBook = ( __bridge_transfer id ) addressBookRef ;
ABAddressBookRequestAccessWithCompletion ( addressBookRef , ^( bool granted , CFErrorRef requestAccessError ) {
ABAddressBookRequestAccessWithCompletion ( addressBookRef , ^( bool granted , CFErrorRef requestAccessError ) {
if ( granted ) {
if ( granted ) {
@ -138,10 +162,11 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
[ futureAddressBookSource trySetResult : addressBook ] ;
[ futureAddressBookSource trySetResult : addressBook ] ;
} ) ;
} ) ;
} else {
} else {
[ self blockingContactDialog ] ;
[ futureAddressBookSource trySetFailure : ( __bridge id ) requestAccessError ] ;
[ futureAddressBookSource trySetFailure : ( __bridge id ) requestAccessError ] ;
}
}
} ) ;
} ) ;
return futureAddressBookSource . future ;
return futureAddressBookSource . future ;
}
}
@ -155,7 +180,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
( void * ) ( unsigned long ) ABPersonGetSortOrdering ( ) ) ;
( void * ) ( unsigned long ) ABPersonGetSortOrdering ( ) ) ;
NSArray * sortedPeople = ( __bridge_transfer NSArray * ) allPeopleMutable ;
NSArray * sortedPeople = ( __bridge_transfer NSArray * ) allPeopleMutable ;
/ / This predicate returns all contacts from the addressbook having at least one phone number
/ / This predicate returns all contacts from the addressbook having at least one phone number
NSPredicate * predicate = [ NSPredicate predicateWithBlock : ^BOOL ( id record , NSDictionary * bindings ) {
NSPredicate * predicate = [ NSPredicate predicateWithBlock : ^BOOL ( id record , NSDictionary * bindings ) {
@ -190,11 +215,11 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
- ( Contact * ) contactForRecord : ( ABRecordRef ) record {
- ( Contact * ) contactForRecord : ( ABRecordRef ) record {
ABRecordID recordID = ABRecordGetRecordID ( record ) ;
ABRecordID recordID = ABRecordGetRecordID ( record ) ;
NSString * firstName = ( __bridge_transfer NSString * ) ABRecordCopyValue ( record , kABPersonFirstNameProperty ) ;
NSString * firstName = ( __bridge_transfer NSString * ) ABRecordCopyValue ( record , kABPersonFirstNameProperty ) ;
NSString * lastName = ( __bridge_transfer NSString * ) ABRecordCopyValue ( record , kABPersonLastNameProperty ) ;
NSString * lastName = ( __bridge_transfer NSString * ) ABRecordCopyValue ( record , kABPersonLastNameProperty ) ;
NSArray * phoneNumbers = [ self phoneNumbersForRecord : record ] ;
NSArray * phoneNumbers = [ self phoneNumbersForRecord : record ] ;
if ( !firstName && !lastName ) {
if ( !firstName && !lastName ) {
NSString * companyName = ( __bridge_transfer NSString * ) ABRecordCopyValue ( record , kABPersonOrganizationProperty ) ;
NSString * companyName = ( __bridge_transfer NSString * ) ABRecordCopyValue ( record , kABPersonOrganizationProperty ) ;
if ( companyName ) {
if ( companyName ) {
@ -203,12 +228,12 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
firstName = phoneNumbers . firstObject ;
firstName = phoneNumbers . firstObject ;
}
}
}
}
NSString * notes = ( __bridge_transfer NSString * ) ABRecordCopyValue ( record , kABPersonNoteProperty ) ;
NSString * notes = ( __bridge_transfer NSString * ) ABRecordCopyValue ( record , kABPersonNoteProperty ) ;
NSArray * emails = [ ContactsManager emailsForRecord : record ] ;
NSArray * emails = [ ContactsManager emailsForRecord : record ] ;
NSData * image = ( __bridge_transfer NSData * ) ABPersonCopyImageDataWithFormat ( record , kABPersonImageFormatThumbnail ) ;
NSData * image = ( __bridge_transfer NSData * ) ABPersonCopyImageDataWithFormat ( record , kABPersonImageFormatThumbnail ) ;
UIImage * img = [ UIImage imageWithData : image ] ;
UIImage * img = [ UIImage imageWithData : image ] ;
return [ Contact contactWithFirstName : firstName
return [ Contact contactWithFirstName : firstName
andLastName : lastName
andLastName : lastName
andUserTextPhoneNumbers : phoneNumbers
andUserTextPhoneNumbers : phoneNumbers
@ -220,7 +245,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
- ( Contact * ) latestContactForPhoneNumber : ( PhoneNumber * ) phoneNumber {
- ( Contact * ) latestContactForPhoneNumber : ( PhoneNumber * ) phoneNumber {
NSArray * allContacts = [ self allContacts ] ;
NSArray * allContacts = [ self allContacts ] ;
ContactSearchBlock searchBlock = ^BOOL ( Contact * contact , NSUInteger idx , BOOL * stop ) {
ContactSearchBlock searchBlock = ^BOOL ( Contact * contact , NSUInteger idx , BOOL * stop ) {
for ( PhoneNumber * number in contact . parsedPhoneNumbers ) {
for ( PhoneNumber * number in contact . parsedPhoneNumbers ) {
@ -231,9 +256,9 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
}
}
return NO ;
return NO ;
} ;
} ;
NSUInteger contactIndex = [ allContacts indexOfObjectPassingTest : searchBlock ] ;
NSUInteger contactIndex = [ allContacts indexOfObjectPassingTest : searchBlock ] ;
if ( contactIndex != NSNotFound ) {
if ( contactIndex != NSNotFound ) {
return allContacts [ contactIndex ] ;
return allContacts [ contactIndex ] ;
} else {
} else {
@ -247,7 +272,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
- ( NSArray * ) phoneNumbersForRecord : ( ABRecordRef ) record {
- ( NSArray * ) phoneNumbersForRecord : ( ABRecordRef ) record {
ABMultiValueRef numberRefs = ABRecordCopyValue ( record , kABPersonPhoneProperty ) ;
ABMultiValueRef numberRefs = ABRecordCopyValue ( record , kABPersonPhoneProperty ) ;
@ try {
@ try {
NSArray * phoneNumbers = ( __bridge_transfer NSArray * ) ABMultiValueCopyArrayOfAllValues ( numberRefs ) ;
NSArray * phoneNumbers = ( __bridge_transfer NSArray * ) ABMultiValueCopyArrayOfAllValues ( numberRefs ) ;
@ -271,7 +296,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
+ ( NSArray * ) emailsForRecord : ( ABRecordRef ) record {
+ ( NSArray * ) emailsForRecord : ( ABRecordRef ) record {
ABMultiValueRef emailRefs = ABRecordCopyValue ( record , kABPersonEmailProperty ) ;
ABMultiValueRef emailRefs = ABRecordCopyValue ( record , kABPersonEmailProperty ) ;
@ try {
@ try {
NSArray * emails = ( __bridge_transfer NSArray * ) ABMultiValueCopyArrayOfAllValues ( emailRefs ) ;
NSArray * emails = ( __bridge_transfer NSArray * ) ABMultiValueCopyArrayOfAllValues ( emailRefs ) ;
@ -288,14 +313,14 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
+ ( NSDictionary * ) groupContactsByFirstLetter : ( NSArray * ) contacts matchingSearchString : ( NSString * ) optionalSearchString {
+ ( NSDictionary * ) groupContactsByFirstLetter : ( NSArray * ) contacts matchingSearchString : ( NSString * ) optionalSearchString {
require ( contacts != nil ) ;
require ( contacts != nil ) ;
NSArray * matchingContacts = [ contacts filter : ^int ( Contact * contact ) {
NSArray * matchingContacts = [ contacts filter : ^int ( Contact * contact ) {
return optionalSearchString . length == 0 || [ self name : contact . fullName matchesQuery : optionalSearchString ] ;
return optionalSearchString . length == 0 || [ self name : contact . fullName matchesQuery : optionalSearchString ] ;
} ] ;
} ] ;
return [ matchingContacts groupBy : ^id ( Contact * contact ) {
return [ matchingContacts groupBy : ^id ( Contact * contact ) {
NSString * nameToUse = @ "";
NSString * nameToUse = @ "";
BOOL firstNameOrdering = ABPersonGetSortOrdering ( ) == kABPersonCompositeNameFormatFirstNameFirst ?YES : NO ;
BOOL firstNameOrdering = ABPersonGetSortOrdering ( ) == kABPersonCompositeNameFormatFirstNameFirst ?YES : NO ;
if ( firstNameOrdering && contact . firstName != nil && contact . firstName . length > 0 ) {
if ( firstNameOrdering && contact . firstName != nil && contact . firstName . length > 0 ) {
@ -355,7 +380,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
NSCharacterSet * whitespaceSet = NSCharacterSet . whitespaceCharacterSet ;
NSCharacterSet * whitespaceSet = NSCharacterSet . whitespaceCharacterSet ;
NSArray * queryStrings = [ queryString componentsSeparatedByCharactersInSet : whitespaceSet ] ;
NSArray * queryStrings = [ queryString componentsSeparatedByCharactersInSet : whitespaceSet ] ;
NSArray * nameStrings = [ nameString componentsSeparatedByCharactersInSet : whitespaceSet ] ;
NSArray * nameStrings = [ nameString componentsSeparatedByCharactersInSet : whitespaceSet ] ;
return [ queryStrings all : ^int ( NSString * query ) {
return [ queryStrings all : ^int ( NSString * query ) {
if ( query . length == 0 ) return YES ;
if ( query . length == 0 ) return YES ;
return [ nameStrings any : ^int ( NSString * nameWord ) {
return [ nameStrings any : ^int ( NSString * nameWord ) {
@ -368,7 +393,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
+ ( BOOL ) phoneNumber : ( PhoneNumber * ) phoneNumber matchesQuery : ( NSString * ) queryString {
+ ( BOOL ) phoneNumber : ( PhoneNumber * ) phoneNumber matchesQuery : ( NSString * ) queryString {
NSString * phoneNumberString = phoneNumber . localizedDescriptionForUser ;
NSString * phoneNumberString = phoneNumber . localizedDescriptionForUser ;
NSString * searchString = phoneNumberString . digitsOnly ;
NSString * searchString = phoneNumberString . digitsOnly ;
if ( queryString . length == 0 ) return YES ;
if ( queryString . length == 0 ) return YES ;
NSStringCompareOptions searchOpts = NSCaseInsensitiveSearch | NSAnchoredSearch ;
NSStringCompareOptions searchOpts = NSCaseInsensitiveSearch | NSAnchoredSearch ;
return [ searchString rangeOfString : queryString options : searchOpts ] . location != NSNotFound ;
return [ searchString rangeOfString : queryString options : searchOpts ] . location != NSNotFound ;
@ -389,7 +414,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
#pragma mark - Whisper User Management
#pragma mark - Whisper User Management
- ( NSArray * ) getSignalUsersFromContactsArray : ( NSArray * ) contacts {
- ( NSArray * ) getSignalUsersFromContactsArray : ( NSArray * ) contacts {
return [ [ contacts filter : ^int ( Contact * contact ) {
return [ [ contacts filter : ^int ( Contact * contact ) {
return [ self isContactRegisteredWithRedPhone : contact ] || contact . isTextSecureContact ;
return [ self isContactRegisteredWithRedPhone : contact ] || contact . isTextSecureContact ;
} ] sortedArrayUsingComparator : [ [ self class ] contactComparator ] ] ;
} ] sortedArrayUsingComparator : [ [ self class ] contactComparator ] ] ;
}
}
@ -420,25 +445,25 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
}
}
- ( NSArray * ) getNewItemsFrom : ( NSArray * ) newArray comparedTo : ( NSArray * ) oldArray {
- ( NSArray * ) getNewItemsFrom : ( NSArray * ) newArray comparedTo : ( NSArray * ) oldArray {
NSMutableSet * newSet = [ NSMutableSet setWithArray : newArray ] ;
NSMutableSet * newSet = [ NSMutableSet setWithArray : newArray ] ;
NSSet * oldSet = [ NSSet setWithArray : oldArray ] ;
NSSet * oldSet = [ NSSet setWithArray : oldArray ] ;
[ newSet minusSet : oldSet ] ;
[ newSet minusSet : oldSet ] ;
return newSet . allObjects ;
return newSet . allObjects ;
}
}
- ( BOOL ) isContactRegisteredWithRedPhone : ( Contact * ) contact {
- ( BOOL ) isContactRegisteredWithRedPhone : ( Contact * ) contact {
for ( PhoneNumber * phoneNumber in contact . parsedPhoneNumbers ) {
for ( PhoneNumber * phoneNumber in contact . parsedPhoneNumbers ) {
if ( [ self isPhoneNumberRegisteredWithRedPhone : phoneNumber ] ) {
if ( [ self isPhoneNumberRegisteredWithRedPhone : phoneNumber ] ) {
return YES ;
return YES ;
}
}
}
}
return NO ;
return NO ;
}
}
- ( BOOL ) isPhoneNumberRegisteredWithRedPhone : ( PhoneNumber * ) phoneNumber {
- ( BOOL ) isPhoneNumberRegisteredWithRedPhone : ( PhoneNumber * ) phoneNumber {
PhoneNumberDirectoryFilter * directory = Environment . getCurrent . phoneDirectoryManager . getCurrentFilter ;
PhoneNumberDirectoryFilter * directory = Environment . getCurrent . phoneDirectoryManager . getCurrentFilter ;
return phoneNumber != nil && [ directory containsPhoneNumber : phoneNumber ] ;
return phoneNumber != nil && [ directory containsPhoneNumber : phoneNumber ] ;
}
}
- ( NSString * ) nameStringForPhoneIdentifier : ( NSString * ) identifier {
- ( NSString * ) nameStringForPhoneIdentifier : ( NSString * ) identifier {