2014-05-06 19:41:08 +02:00
|
|
|
#import "ContactsManager.h"
|
|
|
|
#import <AddressBook/AddressBook.h>
|
2014-08-09 01:01:05 +02:00
|
|
|
#import <libPhoneNumber-iOS/NBPhoneNumber.h>
|
2014-05-06 19:41:08 +02:00
|
|
|
#import "Environment.h"
|
|
|
|
#import "NotificationManifest.h"
|
|
|
|
#import "PhoneNumberDirectoryFilter.h"
|
|
|
|
#import "PhoneNumberDirectoryFilterManager.h"
|
|
|
|
#import "PreferencesUtil.h"
|
|
|
|
#import "Util.h"
|
|
|
|
|
|
|
|
#define ADDRESSBOOK_QUEUE dispatch_get_main_queue()
|
|
|
|
|
|
|
|
typedef BOOL (^ContactSearchBlock)(id, NSUInteger, BOOL*);
|
|
|
|
|
|
|
|
@interface ContactsManager () {
|
|
|
|
id addressBookReference;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation ContactsManager
|
|
|
|
|
|
|
|
- (id)init {
|
|
|
|
self = [super init];
|
|
|
|
if (self) {
|
2014-11-21 14:38:37 +01:00
|
|
|
life = [TOCCancelTokenSource new];
|
|
|
|
observableContactsController = [ObservableValueController observableValueControllerWithInitialValue:nil];
|
|
|
|
observableRedPhoneUsersController = [ObservableValueController observableValueControllerWithInitialValue:nil];
|
2014-05-06 19:41:08 +02:00
|
|
|
[self registerNotificationHandlers];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
-(void) doAfterEnvironmentInitSetup {
|
|
|
|
[self setupAddressBook];
|
|
|
|
[observableContactsController watchLatestValueOnArbitraryThread:^(NSArray *latestContacts) {
|
|
|
|
@synchronized(self) {
|
|
|
|
[self setupLatestContacts:latestContacts];
|
|
|
|
}
|
2014-08-20 14:08:32 +02:00
|
|
|
} untilCancelled:life.token];
|
2014-05-06 19:41:08 +02:00
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
[observableRedPhoneUsersController watchLatestValueOnArbitraryThread:^(NSArray *latestUsers) {
|
2014-05-06 19:41:08 +02:00
|
|
|
@synchronized(self) {
|
2014-11-21 14:38:37 +01:00
|
|
|
[self setupLatestRedPhoneUsers:latestUsers];
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
2014-08-20 14:08:32 +02:00
|
|
|
} untilCancelled:life.token];
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
-(void)dealloc {
|
|
|
|
[life cancel];
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Notification Handlers
|
|
|
|
-(void) registerNotificationHandlers{
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatedDirectoryHandler:) name:NOTIFICATION_DIRECTORY_UPDATE object:nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void) updatedDirectoryHandler:(NSNotification*) notification {
|
2014-11-25 16:38:33 +01:00
|
|
|
NSArray *currentUsers = [self getSignalUsersFromContactsArray:latestContactsById.allValues];
|
2014-11-21 14:38:37 +01:00
|
|
|
|
|
|
|
[observableRedPhoneUsersController updateValue:currentUsers];
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Address Book callbacks
|
|
|
|
|
|
|
|
void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef info, void *context);
|
|
|
|
void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef info, void *context) {
|
|
|
|
ContactsManager* contactsManager = (__bridge ContactsManager*)context;
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
[contactsManager pullLatestAddressBook];
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Setup
|
|
|
|
|
|
|
|
-(void) setupAddressBook {
|
|
|
|
dispatch_async(ADDRESSBOOK_QUEUE, ^{
|
|
|
|
[[ContactsManager asyncGetAddressBook] thenDo:^(id addressBook) {
|
|
|
|
addressBookReference = addressBook;
|
|
|
|
ABAddressBookRef cfAddressBook = (__bridge ABAddressBookRef)addressBook;
|
|
|
|
ABAddressBookRegisterExternalChangeCallback(cfAddressBook, onAddressBookChanged, (__bridge void*)self);
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
|
|
|
|
[self pullLatestAddressBook];
|
|
|
|
});
|
|
|
|
}];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void) pullLatestAddressBook{
|
|
|
|
CFErrorRef creationError = nil;
|
|
|
|
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &creationError);
|
|
|
|
checkOperationDescribe(nil == creationError, [((__bridge NSError *)creationError) localizedDescription]) ;
|
|
|
|
ABAddressBookRequestAccessWithCompletion(addressBookRef, nil);
|
|
|
|
[observableContactsController updateValue:[self getContactsFromAddressBook:addressBookRef]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setupLatestContacts:(NSArray *)contacts {
|
|
|
|
if (contacts) {
|
|
|
|
latestContactsById = [ContactsManager keyContactsById:contacts];
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
2014-11-21 14:38:37 +01:00
|
|
|
[self updatedDirectoryHandler:nil];
|
2014-05-06 19:41:08 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
- (void)setupLatestRedPhoneUsers:(NSArray *)users {
|
2014-05-06 19:41:08 +02:00
|
|
|
if (users) {
|
|
|
|
latestWhisperUsersById = [ContactsManager keyContactsById:users];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Observables
|
|
|
|
|
2014-12-29 23:55:57 +01:00
|
|
|
-(ObservableValue *)getObservableContacts {
|
2014-05-06 19:41:08 +02:00
|
|
|
return observableContactsController;
|
|
|
|
}
|
|
|
|
|
2014-12-29 23:55:57 +01:00
|
|
|
-(ObservableValue *)getObservableRedPhoneUsers {
|
2014-11-21 14:38:37 +01:00
|
|
|
return observableRedPhoneUsersController;
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Address Book utils
|
|
|
|
|
2014-08-20 14:08:32 +02:00
|
|
|
+(TOCFuture*) asyncGetAddressBook {
|
2014-05-06 19:41:08 +02:00
|
|
|
CFErrorRef creationError = nil;
|
|
|
|
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &creationError);
|
|
|
|
assert((addressBookRef == nil) == (creationError != nil));
|
|
|
|
if (creationError != nil) {
|
2014-08-20 14:08:32 +02:00
|
|
|
return [TOCFuture futureWithFailure:(__bridge_transfer id)creationError];
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
2014-08-20 14:08:32 +02:00
|
|
|
TOCFutureSource *futureAddressBookSource = [TOCFutureSource new];
|
2014-05-06 19:41:08 +02:00
|
|
|
|
|
|
|
id addressBook = (__bridge_transfer id)addressBookRef;
|
|
|
|
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef requestAccessError) {
|
|
|
|
if (granted) {
|
|
|
|
dispatch_async(ADDRESSBOOK_QUEUE,^{
|
|
|
|
[futureAddressBookSource trySetResult:addressBook];
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
[futureAddressBookSource trySetFailure:(__bridge id)requestAccessError];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-08-20 14:08:32 +02:00
|
|
|
return futureAddressBookSource.future;
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
-(NSArray*) getContactsFromAddressBook:(ABAddressBookRef)addressBook {
|
2014-07-31 15:50:24 +02:00
|
|
|
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
|
|
|
|
CFMutableArrayRef allPeopleMutable = CFArrayCreateMutableCopy(kCFAllocatorDefault,
|
|
|
|
CFArrayGetCount(allPeople),allPeople);
|
|
|
|
|
|
|
|
CFArraySortValues(allPeopleMutable,CFRangeMake(0, CFArrayGetCount(allPeopleMutable)),
|
|
|
|
(CFComparatorFunction)ABPersonComparePeopleByName,
|
|
|
|
(void*)(unsigned long)ABPersonGetSortOrdering());
|
|
|
|
|
|
|
|
NSArray *sortedPeople = (__bridge_transfer NSArray *)allPeopleMutable;
|
2014-08-09 01:01:05 +02:00
|
|
|
|
|
|
|
// This predicate returns all contacts from the addressbook having at least one phone number
|
2014-07-31 15:50:24 +02:00
|
|
|
|
2014-08-02 06:51:10 +02:00
|
|
|
NSPredicate* predicate = [NSPredicate predicateWithBlock: ^BOOL(id record, NSDictionary *bindings) {
|
|
|
|
ABMultiValueRef phoneNumbers = ABRecordCopyValue( (__bridge ABRecordRef)record, kABPersonPhoneProperty);
|
|
|
|
BOOL result = NO;
|
|
|
|
|
|
|
|
for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
|
|
|
|
NSString* phoneNumber = (__bridge_transfer NSString*) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
|
2014-08-14 03:13:24 +02:00
|
|
|
if (phoneNumber.length>0) {
|
2014-08-02 06:51:10 +02:00
|
|
|
result = YES;
|
|
|
|
break;
|
|
|
|
}
|
2014-09-07 20:43:53 +02:00
|
|
|
}
|
2014-08-02 06:51:10 +02:00
|
|
|
CFRelease(phoneNumbers);
|
|
|
|
return result;
|
2014-09-07 20:43:53 +02:00
|
|
|
}];
|
2014-07-31 15:50:24 +02:00
|
|
|
CFRelease(allPeople);
|
2014-08-02 06:51:10 +02:00
|
|
|
NSArray* filteredContacts = [sortedPeople filteredArrayUsingPredicate:predicate];
|
2014-07-31 15:50:24 +02:00
|
|
|
|
2014-08-02 06:51:10 +02:00
|
|
|
return [filteredContacts map:^id(id item) {
|
2014-05-06 19:41:08 +02:00
|
|
|
return [self contactForRecord:(__bridge ABRecordRef)item];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(NSArray*)latestContactsWithSearchString:(NSString *)searchString {
|
2014-09-07 20:43:53 +02:00
|
|
|
return [latestContactsById.allValues filter:^int(Contact *contact) {
|
|
|
|
return searchString.length == 0 || [ContactsManager name:contact.fullName matchesQuery:searchString];
|
2014-05-06 19:41:08 +02:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Contact/Phone Number util
|
|
|
|
|
|
|
|
- (Contact *)contactForRecord:(ABRecordRef)record {
|
|
|
|
ABRecordID recordID = ABRecordGetRecordID(record);
|
|
|
|
|
|
|
|
NSString *firstName = (__bridge_transfer NSString*)ABRecordCopyValue(record, kABPersonFirstNameProperty);
|
|
|
|
NSString *lastName = (__bridge_transfer NSString*)ABRecordCopyValue(record, kABPersonLastNameProperty);
|
|
|
|
NSArray *phoneNumbers = [self phoneNumbersForRecord:record];
|
|
|
|
|
|
|
|
if (!firstName && !lastName) {
|
|
|
|
NSString *companyName = (__bridge_transfer NSString*)ABRecordCopyValue(record, kABPersonOrganizationProperty);
|
|
|
|
if (companyName) {
|
|
|
|
firstName = companyName;
|
2014-08-14 03:13:24 +02:00
|
|
|
} else if (phoneNumbers.count) {
|
2014-09-07 20:43:53 +02:00
|
|
|
firstName = phoneNumbers.firstObject;
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NSString *notes = (__bridge_transfer NSString*)ABRecordCopyValue(record, kABPersonNoteProperty);
|
|
|
|
NSArray *emails = [ContactsManager emailsForRecord:record];
|
|
|
|
NSData *image = (__bridge_transfer NSData*)ABPersonCopyImageDataWithFormat(record, kABPersonImageFormatThumbnail);
|
|
|
|
UIImage *img = [UIImage imageWithData:image];
|
|
|
|
|
|
|
|
return [Contact contactWithFirstName:firstName
|
|
|
|
andLastName:lastName
|
|
|
|
andUserTextPhoneNumbers:phoneNumbers
|
|
|
|
andEmails:emails
|
|
|
|
andImage:img
|
|
|
|
andContactID:recordID
|
|
|
|
andNotes:notes];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(Contact*)latestContactForPhoneNumber:(PhoneNumber *)phoneNumber {
|
2014-09-07 20:43:53 +02:00
|
|
|
NSArray *allContacts = latestContactsById.allValues;
|
2014-05-06 19:41:08 +02:00
|
|
|
|
|
|
|
ContactSearchBlock searchBlock = ^BOOL(Contact *contact, NSUInteger idx, BOOL *stop) {
|
|
|
|
for (PhoneNumber *number in contact.parsedPhoneNumbers) {
|
|
|
|
|
|
|
|
if ([self phoneNumber:number matchesNumber:phoneNumber]) {
|
|
|
|
*stop = YES;
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
};
|
|
|
|
|
|
|
|
NSUInteger contactIndex = [allContacts indexOfObjectPassingTest:searchBlock];
|
|
|
|
|
|
|
|
if (contactIndex != NSNotFound) {
|
|
|
|
return allContacts[contactIndex];
|
|
|
|
} else {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)phoneNumber:(PhoneNumber *)phoneNumber1 matchesNumber:(PhoneNumber *)phoneNumber2 {
|
2014-09-07 20:43:53 +02:00
|
|
|
return [phoneNumber1.toE164 isEqualToString:phoneNumber2.toE164];
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *)phoneNumbersForRecord:(ABRecordRef)record {
|
|
|
|
ABMultiValueRef numberRefs = ABRecordCopyValue(record, kABPersonPhoneProperty);
|
|
|
|
|
|
|
|
@try {
|
|
|
|
NSArray *phoneNumbers = (__bridge_transfer NSArray*)ABMultiValueCopyArrayOfAllValues(numberRefs);
|
|
|
|
|
|
|
|
if (phoneNumbers == nil) phoneNumbers = @[];
|
|
|
|
|
|
|
|
NSMutableArray *numbers = [NSMutableArray array];
|
|
|
|
|
2014-08-14 03:13:24 +02:00
|
|
|
for (NSUInteger i = 0; i < phoneNumbers.count; i++) {
|
2014-08-09 01:01:05 +02:00
|
|
|
NSString *phoneNumber = phoneNumbers[i];
|
2014-05-06 19:41:08 +02:00
|
|
|
[numbers addObject:phoneNumber];
|
|
|
|
}
|
|
|
|
|
|
|
|
return numbers;
|
|
|
|
|
|
|
|
} @finally {
|
|
|
|
if (numberRefs) {
|
|
|
|
CFRelease(numberRefs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+(NSArray *)emailsForRecord:(ABRecordRef)record {
|
|
|
|
ABMultiValueRef emailRefs = ABRecordCopyValue(record, kABPersonEmailProperty);
|
|
|
|
|
|
|
|
@try {
|
|
|
|
NSArray *emails = (__bridge_transfer NSArray*)ABMultiValueCopyArrayOfAllValues(emailRefs);
|
|
|
|
|
|
|
|
if (emails == nil) emails = @[];
|
|
|
|
|
|
|
|
return emails;
|
|
|
|
|
|
|
|
} @finally {
|
|
|
|
if (emailRefs) {
|
|
|
|
CFRelease(emailRefs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+(NSDictionary *)groupContactsByFirstLetter:(NSArray *)contacts matchingSearchString:(NSString *)optionalSearchString {
|
|
|
|
require(contacts != nil);
|
|
|
|
|
|
|
|
NSArray *matchingContacts = [contacts filter:^int(Contact *contact) {
|
2014-09-07 20:43:53 +02:00
|
|
|
return optionalSearchString.length == 0 || [self name:contact.fullName matchesQuery:optionalSearchString];
|
2014-05-06 19:41:08 +02:00
|
|
|
}];
|
|
|
|
|
|
|
|
return [matchingContacts groupBy:^id(Contact *contact) {
|
2014-11-03 20:33:50 +01:00
|
|
|
NSString *nameToUse = @"";
|
2014-05-06 19:41:08 +02:00
|
|
|
|
|
|
|
BOOL firstNameOrdering = ABPersonGetSortOrdering() == kABPersonCompositeNameFormatFirstNameFirst?YES:NO;
|
|
|
|
|
2014-09-07 20:43:53 +02:00
|
|
|
if (firstNameOrdering && contact.firstName != nil && contact.firstName.length > 0) {
|
|
|
|
nameToUse = contact.firstName;
|
|
|
|
} else if (!firstNameOrdering && contact.lastName != nil && contact.lastName.length > 0){
|
|
|
|
nameToUse = contact.lastName;
|
|
|
|
} else if (contact.lastName == nil) {
|
|
|
|
if (contact.fullName.length > 0) {
|
|
|
|
nameToUse = contact.fullName;
|
2014-05-06 19:41:08 +02:00
|
|
|
} else {
|
|
|
|
return nameToUse;
|
|
|
|
}
|
|
|
|
} else {
|
2014-09-07 20:43:53 +02:00
|
|
|
nameToUse = contact.lastName;
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
2014-11-03 20:33:50 +01:00
|
|
|
if (nameToUse.length >= 1) {
|
|
|
|
return [[[nameToUse substringToIndex:1] uppercaseString] decomposedStringWithCompatibilityMapping];
|
|
|
|
} else{
|
|
|
|
return @" ";
|
|
|
|
}
|
2014-05-06 19:41:08 +02:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
+(NSDictionary *)keyContactsById:(NSArray *)contacts {
|
|
|
|
return [contacts keyedBy:^id(Contact* contact) {
|
2014-08-13 02:02:29 +02:00
|
|
|
return @((int)contact.recordID);
|
2014-05-06 19:41:08 +02:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(Contact *)latestContactWithRecordId:(ABRecordID)recordId {
|
|
|
|
@synchronized(self) {
|
2014-08-13 02:02:29 +02:00
|
|
|
return latestContactsById[@(recordId)];
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
- (NSArray*)allContacts {
|
|
|
|
return [latestContactsById allValues];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)recordsForContacts:(NSArray*) contacts{
|
2014-05-06 19:41:08 +02:00
|
|
|
return [contacts map:^id(Contact *contact) {
|
2014-08-13 02:02:29 +02:00
|
|
|
return @([contact recordID]);
|
2014-05-06 19:41:08 +02:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
+(BOOL)name:(NSString *)nameString matchesQuery:(NSString *)queryString {
|
2014-09-08 01:31:05 +02:00
|
|
|
NSCharacterSet *whitespaceSet = NSCharacterSet.whitespaceCharacterSet;
|
2014-05-06 19:41:08 +02:00
|
|
|
NSArray *queryStrings = [queryString componentsSeparatedByCharactersInSet:whitespaceSet];
|
|
|
|
NSArray *nameStrings = [nameString componentsSeparatedByCharactersInSet:whitespaceSet];
|
|
|
|
|
|
|
|
return [queryStrings all:^int(NSString* query) {
|
2014-08-14 03:13:24 +02:00
|
|
|
if (query.length == 0) return YES;
|
2014-05-06 19:41:08 +02:00
|
|
|
return [nameStrings any:^int(NSString* nameWord) {
|
|
|
|
NSStringCompareOptions searchOpts = NSCaseInsensitiveSearch | NSAnchoredSearch;
|
|
|
|
return [nameWord rangeOfString:query options:searchOpts].location != NSNotFound;
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
+(BOOL)phoneNumber:(PhoneNumber *)phoneNumber matchesQuery:(NSString *)queryString {
|
2014-09-07 20:43:53 +02:00
|
|
|
NSString *phoneNumberString = phoneNumber.localizedDescriptionForUser;
|
|
|
|
NSString *searchString = phoneNumberString.digitsOnly;
|
2014-05-06 19:41:08 +02:00
|
|
|
|
2014-08-14 03:13:24 +02:00
|
|
|
if (queryString.length == 0) return YES;
|
2014-05-06 19:41:08 +02:00
|
|
|
NSStringCompareOptions searchOpts = NSCaseInsensitiveSearch | NSAnchoredSearch;
|
|
|
|
return [searchString rangeOfString:queryString options:searchOpts].location != NSNotFound;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(NSArray*) contactsForContactIds:(NSArray *)contactIds {
|
|
|
|
NSMutableArray *contacts = [NSMutableArray array];
|
|
|
|
for (NSNumber *favouriteId in contactIds) {
|
2014-11-19 05:17:06 +01:00
|
|
|
Contact *contact = [self latestContactWithRecordId:favouriteId.intValue];
|
2014-05-06 19:41:08 +02:00
|
|
|
|
|
|
|
if (contact) {
|
|
|
|
[contacts addObject:contact];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [contacts copy];
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Whisper User Management
|
|
|
|
|
2014-11-25 16:38:33 +01:00
|
|
|
-(NSArray*) getSignalUsersFromContactsArray:(NSArray*)contacts {
|
2014-11-21 14:38:37 +01:00
|
|
|
return [contacts filter:^int(Contact* contact) {
|
2014-11-25 16:38:33 +01:00
|
|
|
return [self isContactRegisteredWithRedPhone:contact] || contact.isTextSecureContact;
|
2014-11-21 14:38:37 +01:00
|
|
|
}];
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
2014-11-21 14:38:37 +01:00
|
|
|
-(NSArray*) textSecureContacts {
|
|
|
|
return [[self.allContacts filter:^int(Contact* contact) {
|
|
|
|
return [contact isTextSecureContact];
|
|
|
|
}] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
|
|
|
|
Contact *contact1 = (Contact*)obj1;
|
|
|
|
Contact *contact2 = (Contact*)obj2;
|
|
|
|
|
|
|
|
BOOL firstNameOrdering = ABPersonGetSortOrdering() == kABPersonCompositeNameFormatFirstNameFirst?YES:NO;
|
|
|
|
|
|
|
|
if (firstNameOrdering) {
|
|
|
|
return [contact1.firstName compare:contact2.firstName];
|
|
|
|
} else {
|
2014-12-11 17:37:48 +01:00
|
|
|
return [contact1.lastName compare:contact2.lastName];
|
2014-11-21 14:38:37 +01:00
|
|
|
}
|
2014-05-06 19:41:08 +02:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(NSArray*) getNewItemsFrom:(NSArray*) newArray comparedTo:(NSArray*) oldArray {
|
|
|
|
NSMutableSet *newSet = [NSMutableSet setWithArray:newArray];
|
|
|
|
NSSet *oldSet = [NSSet setWithArray:oldArray];
|
|
|
|
|
|
|
|
[newSet minusSet:oldSet];
|
2014-09-08 01:31:05 +02:00
|
|
|
return newSet.allObjects;
|
2014-05-06 19:41:08 +02:00
|
|
|
}
|
|
|
|
|
2014-11-25 17:28:42 +01:00
|
|
|
- (BOOL)isContactRegisteredWithRedPhone:(Contact*)contact {
|
2014-05-06 19:41:08 +02:00
|
|
|
for(PhoneNumber *phoneNumber in contact.parsedPhoneNumbers){
|
2014-11-21 14:38:37 +01:00
|
|
|
if ( [self isPhoneNumberRegisteredWithRedPhone:phoneNumber]) {
|
2014-05-06 19:41:08 +02:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2014-11-25 16:38:33 +01:00
|
|
|
- (BOOL)isPhoneNumberRegisteredWithRedPhone:(PhoneNumber*)phoneNumber {
|
2014-09-08 01:31:05 +02:00
|
|
|
PhoneNumberDirectoryFilter* directory = Environment.getCurrent.phoneDirectoryManager.getCurrentFilter;
|
2014-05-06 19:41:08 +02:00
|
|
|
return phoneNumber != nil && [directory containsPhoneNumber:phoneNumber];
|
|
|
|
}
|
|
|
|
|
2014-11-23 21:57:18 +01:00
|
|
|
- (NSString*)nameStringForPhoneIdentifier:(NSString*)identifier{
|
2015-01-04 18:19:09 +01:00
|
|
|
for (Contact *contact in self.allContacts) {
|
2014-11-23 21:57:18 +01:00
|
|
|
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
|
|
|
|
if ([phoneNumber.toE164 isEqualToString:identifier]) {
|
|
|
|
return contact.fullName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2014-11-25 19:06:09 +01:00
|
|
|
- (UIImage*)imageForPhoneIdentifier:(NSString*)identifier{
|
2015-01-04 18:19:09 +01:00
|
|
|
for (Contact *contact in self.allContacts) {
|
2014-11-25 19:06:09 +01:00
|
|
|
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
|
|
|
|
if ([phoneNumber.toE164 isEqualToString:identifier]) {
|
|
|
|
return contact.image;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2014-05-06 19:41:08 +02:00
|
|
|
@end
|