chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:35:01 +08:00
commit a2e318a963
2974 changed files with 158180 additions and 0 deletions
@@ -0,0 +1,23 @@
//
// TLMobileContactHelper.h
// TLChat
//
// Created by 李伯坤 on 2018/1/9.
// Copyright © 2018年 李伯坤. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TLMobileContactModel.h"
@interface TLMobileContactHelper : NSObject
/**
* 获取通讯录好友
*
* @param success 获取成功,异步返回(通讯录列表,格式化的通讯录列表,格式化的通讯录列表组标题)
* @param failed 获取失败
*/
+ (void)tryToGetAllContactsSuccess:(void (^)(NSArray *data, NSArray *formatData, NSArray *headers))success
failed:(void (^)())failed;
@end
@@ -0,0 +1,208 @@
//
// TLMobileContactHelper.m
// TLChat
//
// Created by 李伯坤 on 2018/1/9.
// Copyright © 2018年 李伯坤. All rights reserved.
//
#import "TLMobileContactHelper.h"
#import <AddressBookUI/AddressBookUI.h>
#import "NSFileManager+TLChat.h"
#import "TLUserGroup.h"
@implementation TLMobileContactHelper
+ (void)tryToGetAllContactsSuccess:(void (^)(NSArray *data, NSArray *formatData, NSArray *headers))success
failed:(void (^)())failed
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 1、获取通讯录信息
ABAddressBookRef addressBooks = nil;
if ([[UIDevice currentDevice].systemVersion floatValue] >= 6.0) {
addressBooks = ABAddressBookCreateWithOptions(NULL, NULL);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBooks, ^(bool granted, CFErrorRef error){
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
else {
addressBooks = ABAddressBookCreate();
}
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBooks);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBooks);
// 2、加载缓存
if (allPeople != nil && CFArrayGetCount(allPeople) > 0) {
NSString *path = [NSFileManager pathContactsData];
NSDictionary *dic = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
if (dic) {
NSArray *data = dic[@"data"];
NSArray *formatData = dic[@"formatData"];
NSArray *headers = dic[@"headers"];
dispatch_async(dispatch_get_main_queue(), ^{
success(data, formatData, headers);
});
}
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
failed();
});
return;
}
// 3、格式转换
NSMutableArray *data = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < nPeople; i++) {
TLMobileContactModel *contact = [[TLMobileContactModel alloc] init];
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
CFTypeRef abName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
CFTypeRef abLastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
CFStringRef abFullName = ABRecordCopyCompositeName(person);
NSString *nameString = (__bridge NSString *)abName;
NSString *lastNameString = (__bridge NSString *)abLastName;
if ((__bridge id)abFullName != nil) {
nameString = (__bridge NSString *)abFullName;
}
else {
if ((__bridge id)abLastName != nil) {
nameString = [NSString stringWithFormat:@"%@ %@", nameString, lastNameString];
}
}
contact.name = nameString;
contact.recordID = (int)ABRecordGetRecordID(person);;
if(ABPersonHasImageData(person)) {
NSData *imageData = (__bridge NSData *)ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);
NSString *imageName = [NSString stringWithFormat:@"%.0lf.jpg", [NSDate date].timeIntervalSince1970 * 10000];
NSString *imagePath = [NSFileManager pathContactsAvatar:imageName];
[imageData writeToFile:imagePath atomically:YES];
contact.avatarPath = imageName;
}
ABPropertyID multiProperties[] = {
kABPersonPhoneProperty,
kABPersonEmailProperty
};
NSInteger multiPropertiesTotal = sizeof(multiProperties) / sizeof(ABPropertyID);
for (NSInteger j = 0; j < multiPropertiesTotal; j++) {
ABPropertyID property = multiProperties[j];
ABMultiValueRef valuesRef = ABRecordCopyValue(person, property);
NSInteger valuesCount = 0;
if (valuesRef != nil) valuesCount = ABMultiValueGetCount(valuesRef);
if (valuesCount == 0) {
CFRelease(valuesRef);
continue;
}
for (NSInteger k = 0; k < valuesCount; k++) {
CFTypeRef value = ABMultiValueCopyValueAtIndex(valuesRef, k);
switch (j) {
case 0: {// Phone number
contact.tel = (__bridge NSString*)value;
break;
}
case 1: {// Email
contact.email = (__bridge NSString*)value;
break;
}
}
CFRelease(value);
}
CFRelease(valuesRef);
}
[data addObject:contact];
if (abName) CFRelease(abName);
if (abLastName) CFRelease(abLastName);
if (abFullName) CFRelease(abFullName);
}
// 4、排序
NSArray *serializeArray = [data sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int i;
NSString *strA = ((TLMobileContactModel *)obj1).pinyin;
NSString *strB = ((TLMobileContactModel *)obj2).pinyin;
for (i = 0; i < strA.length && i < strB.length; i ++) {
char a = toupper([strA characterAtIndex:i]);
char b = toupper([strB characterAtIndex:i]);
if (a > b) {
return (NSComparisonResult)NSOrderedDescending;
}
else if (a < b) {
return (NSComparisonResult)NSOrderedAscending;
}
}
if (strA.length > strB.length) {
return (NSComparisonResult)NSOrderedDescending;
}
else if (strA.length < strB.length){
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];
// 5、分组
data = [[NSMutableArray alloc] init];
NSMutableArray *headers = [[NSMutableArray alloc] initWithObjects:UITableViewIndexSearch, nil];
char lastC = '1';
TLUserGroup *curGroup;
TLUserGroup *othGroup = [[TLUserGroup alloc] init];
[othGroup setGroupName:@"#"];
othGroup.tag = 27;
for (TLMobileContactModel *contact in serializeArray) {
// 获取拼音失败
if (contact.pinyin == nil || contact.pinyin.length == 0) {
[othGroup addObject:contact];
continue;
}
char c = toupper([contact.pinyin characterAtIndex:0]);
if (!isalpha(c)) { // #组
[othGroup addObject:contact];
}
else if (c != lastC){
if (curGroup && curGroup.count > 0) {
[data addObject:curGroup];
[headers addObject:curGroup.groupName];
}
lastC = c;
curGroup = [[TLUserGroup alloc] init];
[curGroup setGroupName:[NSString stringWithFormat:@"%c", c]];
[curGroup addObject:contact];
[curGroup setTag:(NSInteger)c];
}
else {
[curGroup addObject:contact];
}
}
if (curGroup && curGroup.count > 0) {
[data addObject:curGroup];
[headers addObject:curGroup.groupName];
}
if (othGroup.count > 0) {
[data addObject:othGroup];
[headers addObject:othGroup.groupName];
}
// 6、数据返回
dispatch_async(dispatch_get_main_queue(), ^{
success(serializeArray, data, headers);
});
// 7、存入本地缓存
NSDictionary *dic = @{@"data": serializeArray,
@"formatData": data,
@"headers": headers};
NSString *path = [NSFileManager pathContactsData];
if(![NSKeyedArchiver archiveRootObject:dic toFile:path]){
DDLogError(@"缓存联系人数据失败");
}
});
}
@end
@@ -0,0 +1,41 @@
//
// TLMobileContactModel.h
// TLChat
//
// Created by 李伯坤 on 16/3/8.
// Copyright © 2016年 李伯坤. All rights reserved.
//
/**
* 通讯录 Model
*/
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, TLMobileContactStatus) {
TLMobileContactStatusStranger,
TLMobileContactStatusFriend,
TLMobileContactStatusWait,
};
@interface TLMobileContactModel : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *avatarPath;
@property (nonatomic, strong) NSString *avatarURL;
@property (nonatomic, strong) NSString *tel;
@property (nonatomic, assign) TLMobileContactStatus status;
@property (nonatomic, assign) int recordID;
@property (nonatomic, assign) NSString *email;
@property (nonatomic, strong) NSString *pinyin;
@property (nonatomic, strong) NSString *pinyinInitial;
@end
@@ -0,0 +1,57 @@
//
// TLMobileContactModel.m
// TLChat
//
// Created by 李伯坤 on 16/3/8.
// Copyright © 2016年 李伯坤. All rights reserved.
//
#import "TLMobileContactModel.h"
#import "NSString+PinYin.h"
@implementation TLMobileContactModel
- (id)initWithCoder:(NSCoder *)decoder
{
self.name = [decoder decodeObjectForKey:@"name"];
self.avatarPath = [decoder decodeObjectForKey:@"avatarPath"];
self.avatarURL = [decoder decodeObjectForKey:@"avatarURL"];
self.tel = [decoder decodeObjectForKey:@"tel"];
self.status = [decoder decodeIntegerForKey:@"status"];
self.recordID = [decoder decodeIntForKey:@"recordID"];
self.email = [decoder decodeObjectForKey:@"email"];
self.pinyin = [decoder decodeObjectForKey:@"pinyin"];
self.pinyinInitial = [decoder decodeObjectForKey:@"pinyinInitial"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeObject:self.avatarPath forKey:@"avatarPath"];
[encoder encodeObject:self.avatarURL forKey:@"avatarURL"];
[encoder encodeObject:self.tel forKey:@"tel"];
[encoder encodeInteger:self.status forKey:@"status"];
[encoder encodeInt:self.recordID forKey:@"recordID"];
[encoder encodeObject:self.email forKey:@"email"];
[encoder encodeObject:self.pinyin forKey:@"pinyin"];
[encoder encodeObject:self.pinyinInitial forKey:@"pinyinInitial"];
}
- (NSString *)pinyin
{
if (_pinyin == nil) {
_pinyin = self.name.pinyin;
}
return _pinyin;
}
- (NSString *)pinyinInitial
{
if (_pinyinInitial == nil) {
_pinyinInitial = self.name.pinyinInitial;
}
return _pinyinInitial;
}
@end