|
|
@ -15,16 +15,12 @@ typedef BOOL (^ContactSearchBlock)(id, NSUInteger, BOOL *);
|
|
|
|
|
|
|
|
|
|
|
|
NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
|
|
|
|
NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
|
|
|
|
@"OWSContactsManagerSignalAccountsDidChangeNotification";
|
|
|
|
@"OWSContactsManagerSignalAccountsDidChangeNotification";
|
|
|
|
NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification =
|
|
|
|
|
|
|
|
@"OWSContactsManagerSignalRecipientsDidChangeNotification";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@interface OWSContactsManager ()
|
|
|
|
@interface OWSContactsManager ()
|
|
|
|
|
|
|
|
|
|
|
|
@property (atomic, nullable) CNContactStore *contactStore;
|
|
|
|
@property (atomic, nullable) CNContactStore *contactStore;
|
|
|
|
@property (atomic) id addressBookReference;
|
|
|
|
@property (atomic) id addressBookReference;
|
|
|
|
@property (atomic) TOCFuture *futureAddressBook;
|
|
|
|
@property (atomic) TOCFuture *futureAddressBook;
|
|
|
|
@property (atomic) ObservableValueController *observableContactsController;
|
|
|
|
|
|
|
|
@property (atomic) TOCCancelTokenSource *life;
|
|
|
|
|
|
|
|
@property (nonatomic) BOOL isContactsUpdateInFlight;
|
|
|
|
@property (nonatomic) BOOL isContactsUpdateInFlight;
|
|
|
|
// This reflects the contents of the device phone book and includes
|
|
|
|
// This reflects the contents of the device phone book and includes
|
|
|
|
// contacts that do not correspond to any signal account.
|
|
|
|
// contacts that do not correspond to any signal account.
|
|
|
@ -37,18 +33,12 @@ NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification =
|
|
|
|
|
|
|
|
|
|
|
|
@implementation OWSContactsManager
|
|
|
|
@implementation OWSContactsManager
|
|
|
|
|
|
|
|
|
|
|
|
- (void)dealloc {
|
|
|
|
|
|
|
|
[_life cancel];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (id)init {
|
|
|
|
- (id)init {
|
|
|
|
self = [super init];
|
|
|
|
self = [super init];
|
|
|
|
if (!self) {
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_life = [TOCCancelTokenSource new];
|
|
|
|
|
|
|
|
_observableContactsController = [ObservableValueController observableValueControllerWithInitialValue:nil];
|
|
|
|
|
|
|
|
_avatarCache = [NSCache new];
|
|
|
|
_avatarCache = [NSCache new];
|
|
|
|
_allContacts = @[];
|
|
|
|
_allContacts = @[];
|
|
|
|
_signalAccountMap = @{};
|
|
|
|
_signalAccountMap = @{};
|
|
|
@ -74,14 +64,6 @@ NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification =
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[self setupAddressBookIfNecessary];
|
|
|
|
[self setupAddressBookIfNecessary];
|
|
|
|
|
|
|
|
|
|
|
|
__weak OWSContactsManager *weakSelf = self;
|
|
|
|
|
|
|
|
[self.observableContactsController watchLatestValueOnArbitraryThread:^(NSArray *latestContacts) {
|
|
|
|
|
|
|
|
@synchronized(self) {
|
|
|
|
|
|
|
|
[weakSelf updateSignalAccounts:latestContacts];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
untilCancelled:_life.token];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)verifyABPermission {
|
|
|
|
- (void)verifyABPermission {
|
|
|
@ -100,9 +82,8 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|
|
|
|
|
|
|
|
|
|
|
- (void)handleAddressBookChanged
|
|
|
|
- (void)handleAddressBookChanged
|
|
|
|
{
|
|
|
|
{
|
|
|
|
[self pullLatestAddressBook];
|
|
|
|
|
|
|
|
[self intersectContacts];
|
|
|
|
|
|
|
|
[self.avatarCache removeAllObjects];
|
|
|
|
[self.avatarCache removeAllObjects];
|
|
|
|
|
|
|
|
[self pullLatestAddressBook];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - Setup
|
|
|
|
#pragma mark - Setup
|
|
|
@ -155,7 +136,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|
|
|
{
|
|
|
|
{
|
|
|
|
void (^success)() = ^{
|
|
|
|
void (^success)() = ^{
|
|
|
|
DDLogInfo(@"%@ Successfully intersected contacts.", self.tag);
|
|
|
|
DDLogInfo(@"%@ Successfully intersected contacts.", self.tag);
|
|
|
|
[self fireSignalRecipientsDidChange];
|
|
|
|
[self updateSignalAccounts];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
void (^failure)(NSError *error) = ^(NSError *error) {
|
|
|
|
void (^failure)(NSError *error) = ^(NSError *error) {
|
|
|
|
if ([error.domain isEqualToString:OWSSignalServiceKitErrorDomain]
|
|
|
|
if ([error.domain isEqualToString:OWSSignalServiceKitErrorDomain]
|
|
|
@ -179,70 +160,82 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|
|
|
failure:failure];
|
|
|
|
failure:failure];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)fireSignalRecipientsDidChange
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:OWSContactsManagerSignalRecipientsDidChangeNotification
|
|
|
|
|
|
|
|
object:nil];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (void)pullLatestAddressBook {
|
|
|
|
- (void)pullLatestAddressBook {
|
|
|
|
CFErrorRef creationError = nil;
|
|
|
|
dispatch_async(ADDRESSBOOK_QUEUE, ^{
|
|
|
|
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &creationError);
|
|
|
|
CFErrorRef creationError = nil;
|
|
|
|
checkOperationDescribe(nil == creationError, [((__bridge NSError *)creationError)localizedDescription]);
|
|
|
|
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &creationError);
|
|
|
|
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
|
|
|
|
checkOperationDescribe(nil == creationError, [((__bridge NSError *)creationError)localizedDescription]);
|
|
|
|
if (!granted) {
|
|
|
|
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
|
|
|
|
[OWSContactsManager blockingContactDialog];
|
|
|
|
if (!granted) {
|
|
|
|
}
|
|
|
|
[OWSContactsManager blockingContactDialog];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
NSArray<Contact *> *contacts = [self getContactsFromAddressBook:addressBookRef];
|
|
|
|
|
|
|
|
[self updateWithContacts:contacts];
|
|
|
|
});
|
|
|
|
});
|
|
|
|
[self.observableContactsController updateValue:[self getContactsFromAddressBook:addressBookRef]];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)updateSignalAccounts:(NSArray<Contact *> *)contacts
|
|
|
|
- (void)updateWithContacts:(NSArray<Contact *> *)contacts
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// This will happen off of the main thread.
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
|
|
|
|
NSMutableDictionary<NSString *, SignalAccount *> *signalAccountMap = [NSMutableDictionary new];
|
|
|
|
NSMutableDictionary<NSString *, Contact *> *allContactsMap = [NSMutableDictionary new];
|
|
|
|
NSMutableArray<SignalAccount *> *signalAccounts = [NSMutableArray new];
|
|
|
|
for (Contact *contact in contacts) {
|
|
|
|
NSMutableDictionary<NSString *, Contact *> *allContactsMap = [NSMutableDictionary new];
|
|
|
|
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
|
|
|
|
for (Contact *contact in contacts) {
|
|
|
|
NSString *phoneNumberE164 = phoneNumber.toE164;
|
|
|
|
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
|
|
|
|
if (phoneNumberE164.length > 0) {
|
|
|
|
NSString *phoneNumberE164 = phoneNumber.toE164;
|
|
|
|
allContactsMap[phoneNumberE164] = contact;
|
|
|
|
if (phoneNumberE164.length > 0) {
|
|
|
|
}
|
|
|
|
allContactsMap[phoneNumberE164] = contact;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSArray<SignalRecipient *> *signalRecipients = contact.signalRecipients;
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
for (SignalRecipient *signalRecipient in contact.signalRecipients) {
|
|
|
|
self.allContacts = contacts;
|
|
|
|
for (NSString *recipientId in
|
|
|
|
self.allContactsMap = [allContactsMap copy];
|
|
|
|
[contact.textSecureIdentifiers sortedArrayUsingSelector:@selector(compare:)]) {
|
|
|
|
|
|
|
|
SignalAccount *signalAccount = [[SignalAccount alloc] initWithSignalRecipient:signalRecipient];
|
|
|
|
[self intersectContacts];
|
|
|
|
signalAccount.contact = contact;
|
|
|
|
|
|
|
|
if (signalRecipients.count > 1) {
|
|
|
|
[self updateSignalAccounts];
|
|
|
|
signalAccount.isMultipleAccountContact = YES;
|
|
|
|
});
|
|
|
|
signalAccount.multipleAccountLabel =
|
|
|
|
});
|
|
|
|
[[self class] accountLabelForContact:contact recipientId:recipientId];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (signalAccountMap[signalAccount.recipientId]) {
|
|
|
|
- (void)updateSignalAccounts
|
|
|
|
DDLogInfo(@"Ignoring duplicate contact: %@, %@", signalAccount.recipientId, contact.fullName);
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
NSMutableDictionary<NSString *, SignalAccount *> *signalAccountMap = [NSMutableDictionary new];
|
|
|
|
|
|
|
|
NSMutableArray<SignalAccount *> *signalAccounts = [NSMutableArray new];
|
|
|
|
|
|
|
|
NSArray<Contact *> *contacts = self.allContacts;
|
|
|
|
|
|
|
|
[[TSStorageManager sharedManager].dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
|
|
|
|
|
|
|
for (Contact *contact in contacts) {
|
|
|
|
|
|
|
|
NSArray<SignalRecipient *> *signalRecipients = [contact signalRecipientsWithTransaction:transaction];
|
|
|
|
|
|
|
|
for (SignalRecipient *signalRecipient in
|
|
|
|
|
|
|
|
[signalRecipients sortedArrayUsingSelector:@selector(compare:)]) {
|
|
|
|
|
|
|
|
SignalAccount *signalAccount = [[SignalAccount alloc] initWithSignalRecipient:signalRecipient];
|
|
|
|
|
|
|
|
signalAccount.contact = contact;
|
|
|
|
|
|
|
|
if (signalRecipients.count > 1) {
|
|
|
|
|
|
|
|
signalAccount.isMultipleAccountContact = YES;
|
|
|
|
|
|
|
|
signalAccount.multipleAccountLabel =
|
|
|
|
|
|
|
|
[[self class] accountLabelForContact:contact recipientId:signalRecipient.recipientId];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (signalAccountMap[signalAccount.recipientId]) {
|
|
|
|
|
|
|
|
DDLogInfo(@"Ignoring duplicate contact: %@, %@", signalAccount.recipientId, contact.fullName);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
signalAccountMap[signalAccount.recipientId] = signalAccount;
|
|
|
|
|
|
|
|
[signalAccounts addObject:signalAccount];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
signalAccountMap[signalAccount.recipientId] = signalAccount;
|
|
|
|
|
|
|
|
[signalAccounts addObject:signalAccount];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
self.allContacts = contacts;
|
|
|
|
self.signalAccountMap = [signalAccountMap copy];
|
|
|
|
self.signalAccountMap = [signalAccountMap copy];
|
|
|
|
self.signalAccounts = [signalAccounts copy];
|
|
|
|
self.signalAccounts = [signalAccounts copy];
|
|
|
|
|
|
|
|
self.allContactsMap = [allContactsMap copy];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:OWSContactsManagerSignalAccountsDidChangeNotification
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
object:nil];
|
|
|
|
postNotificationName:OWSContactsManagerSignalAccountsDidChangeNotification
|
|
|
|
|
|
|
|
object:nil];
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -371,12 +364,6 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - Observables
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (ObservableValue *)getObservableContacts {
|
|
|
|
|
|
|
|
return self.observableContactsController;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - Address Book utils
|
|
|
|
#pragma mark - Address Book utils
|
|
|
|
|
|
|
|
|
|
|
|
+ (TOCFuture *)asyncGetAddressBook {
|
|
|
|
+ (TOCFuture *)asyncGetAddressBook {
|
|
|
@ -405,8 +392,10 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|
|
|
return futureAddressBookSource.future;
|
|
|
|
return futureAddressBookSource.future;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSArray *)getContactsFromAddressBook:(ABAddressBookRef _Nonnull)addressBook {
|
|
|
|
- (NSArray<Contact *> *)getContactsFromAddressBook:(ABAddressBookRef _Nonnull)addressBook
|
|
|
|
|
|
|
|
{
|
|
|
|
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
|
|
|
|
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
|
|
|
|
|
|
|
|
|
|
|
|
CFMutableArrayRef allPeopleMutable =
|
|
|
|
CFMutableArrayRef allPeopleMutable =
|
|
|
|
CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(allPeople), allPeople);
|
|
|
|
CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(allPeople), allPeople);
|
|
|
|
|
|
|
|
|
|
|
|