1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-02 09:27:55 +01:00
Telegram/Telegraph/TGPhonebookSignals.m
2015-10-01 19:19:52 +03:00

196 lines
6.0 KiB
Objective-C

#import "TGPhonebookSignals.h"
#import <AddressBook/AddressBook.h>
#import <libkern/OSAtomic.h>
#import "TGPhonebookRecord.h"
static NSMutableDictionary *addressBookChangeBlockById()
{
static NSMutableDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
dict = [[NSMutableDictionary alloc] init];
});
return dict;
}
static OSSpinLock addressBookChangeBlockByIdLock = 0;
static void TGAddressBook_Changed(__unused ABAddressBookRef addressBook, __unused CFDictionaryRef info, void *context)
{
int32_t key = (int32_t)(intptr_t)context;
void (^block)() = nil;
OSSpinLockLock(&addressBookChangeBlockByIdLock);
block = addressBookChangeBlockById()[@(key)];
OSSpinLockUnlock(&addressBookChangeBlockByIdLock);
if (block)
block();
}
@interface TGAddressBook : NSObject
{
ABAddressBookRef _addressBook;
int32_t _changeKey;
void (^_changed)(TGAddressBook *);
}
@end
@implementation TGAddressBook
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
__weak TGAddressBook *weakSelf = self;
static int32_t nextChangeKey = 0;
OSSpinLockLock(&addressBookChangeBlockByIdLock);
_changeKey = nextChangeKey++;
addressBookChangeBlockById()[@(_changeKey)] = [^
{
__strong TGAddressBook *strongSelf = weakSelf;
if (strongSelf != nil)
{
if (strongSelf->_changed)
strongSelf->_changed(strongSelf);
}
} copy];
OSSpinLockUnlock(&addressBookChangeBlockByIdLock);
}
return self;
}
- (void)requestAccess:(void (^)(bool))completion
{
ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, __unused CFErrorRef error)
{
if (completion)
completion(granted);
});
}
- (void)setChanged:(void (^)(TGAddressBook *))changed
{
if (_changed == nil)
{
_changed = [changed copy];
ABAddressBookRegisterExternalChangeCallback(_addressBook, &TGAddressBook_Changed, (void *)(intptr_t)_changeKey);
}
else
_changed = [changed copy];
}
- (void)dealloc
{
OSSpinLockLock(&addressBookChangeBlockByIdLock);
[addressBookChangeBlockById() removeObjectForKey:@(_changeKey)];
OSSpinLockUnlock(&addressBookChangeBlockByIdLock);
if (_addressBook != NULL)
{
ABAddressBookUnregisterExternalChangeCallback(_addressBook, &TGAddressBook_Changed, (void *)(intptr_t)_changeKey);
CFRelease(_addressBook);
}
}
- (NSArray *)records
{
NSMutableArray *array = [[NSMutableArray alloc] init];
CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(_addressBook);
if (people != NULL)
{
CFIndex count = CFArrayGetCount(people);
for (CFIndex i = 0; i < count; i++)
{
ABRecordRef person = CFArrayGetValueAtIndex(people, i);
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString *middleName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonMiddleNameProperty);
NSMutableArray *numbers = [[NSMutableArray alloc] init];
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
if (phones != NULL)
{
CFIndex phoneCount = ABMultiValueGetCount(phones);
for (CFIndex j = 0; j < phoneCount; j++)
{
NSString *number = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, j);
NSString *label = nil;
CFStringRef valueLabel = ABMultiValueCopyLabelAtIndex(phones, j);
if (valueLabel != NULL)
{
label = (__bridge_transfer NSString *)ABAddressBookCopyLocalizedLabel(valueLabel);
CFRelease(valueLabel);
}
[numbers addObject:[[TGPhonebookNumber alloc] initWithPhone:number label:label]];
}
CFRelease(phones);
}
[array addObject:[[TGPhonebookRecord alloc] initWithFirstName:firstName lastName:lastName middleName:middleName phoneNumbers:numbers]];
}
CFRelease(people);
}
return array;
}
@end
@implementation TGPhonebookSignals
+ (SSignal *)authorizedAddressBook
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
TGAddressBook *addressBook = [[TGAddressBook alloc] init];
__weak TGAddressBook *weakAddressBook = addressBook;
[addressBook requestAccess:^(bool success)
{
if (success)
{
__strong TGAddressBook *strongAddressBook = weakAddressBook;
if (strongAddressBook != nil)
[subscriber putNext:strongAddressBook];
else
TGLog(@"(TGAddressBook instance deallocated before returning to subscriber)");
[subscriber putCompletion];
}
else
[subscriber putError:nil];
}];
return [[SBlockDisposable alloc] initWithBlock:^
{
[addressBook description]; // hold reference
}];
}];
}
+ (SSignal *)phonebookRecords
{
return [[self authorizedAddressBook] mapToSignal:^SSignal *(TGAddressBook *addressBook)
{
SSignal *changedSignal = [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
[addressBook setChanged:^(TGAddressBook *addressBook)
{
[subscriber putNext:[addressBook records]];
}];
return nil;
}];
return [[SSignal single:[addressBook records]] then:changedSignal];
}];
}
@end