Merge branch 'charlesmchen/cacheAccountNames'

pull/1/head
Matthew Chen 8 years ago
commit e3527b4084

@ -136,7 +136,7 @@ CHECKOUT OPTIONS:
:commit: 7054e4b13ee5bcd6d524adb6dc9a726e8c466308
:git: https://github.com/WhisperSystems/JSQMessagesViewController.git
SignalServiceKit:
:commit: 33a2b05dcaa0ccfe94db12f0e7f0cdf6e861bddf
:commit: c7cc023541c9c7ee098f08f91949263fe0341625
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:commit: 877ac7438be3ad0b45ef5ca3969574e4b97112bf

@ -9,12 +9,17 @@
#import <SignalServiceKit/ContactsUpdater.h>
#import <SignalServiceKit/OWSError.h>
#import <SignalServiceKit/SignalAccount.h>
#import <SignalServiceKit/TSStorageManager.h>
@import Contacts;
NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
@"OWSContactsManagerSignalAccountsDidChangeNotification";
NSString *const kTSStorageManager_AccountDisplayNames = @"kTSStorageManager_AccountDisplayNames";
NSString *const kTSStorageManager_AccountFirstNames = @"kTSStorageManager_AccountFirstNames";
NSString *const kTSStorageManager_AccountLastNames = @"kTSStorageManager_AccountLastNames";
@interface OWSContactsManager () <SystemContactsFetcherDelegate>
@property (atomic) id addressBookReference;
@ -27,6 +32,11 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
@property (atomic) NSArray<SignalAccount *> *signalAccounts;
@property (atomic) NSDictionary<NSString *, SignalAccount *> *signalAccountMap;
@property (nonatomic, readonly) SystemContactsFetcher *systemContactsFetcher;
@property (atomic) NSDictionary<NSString *, NSString *> *cachedAccountNameMap;
@property (atomic) NSDictionary<NSString *, NSString *> *cachedFirstNameMap;
@property (atomic) NSDictionary<NSString *, NSString *> *cachedLastNameMap;
@end
@implementation OWSContactsManager
@ -46,6 +56,8 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
OWSSingletonAssert();
[self loadCachedDisplayNames];
return self;
}
@ -188,6 +200,9 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
dispatch_async(dispatch_get_main_queue(), ^{
self.signalAccountMap = [signalAccountMap copy];
self.signalAccounts = [signalAccounts copy];
[self updateCachedDisplayNames];
[[NSNotificationCenter defaultCenter]
postNotificationName:OWSContactsManagerSignalAccountsDidChangeNotification
object:nil];
@ -195,7 +210,129 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
});
}
- (void)updateCachedDisplayNames
{
OWSAssert([NSThread isMainThread]);
// Preserve any existing values, so that contacts that have been removed
// from system contacts still show up properly in the app.
NSMutableDictionary<NSString *, NSString *> *cachedAccountNameMap
= (self.cachedAccountNameMap ? [self.cachedAccountNameMap mutableCopy] : [NSMutableDictionary new]);
NSMutableDictionary<NSString *, NSString *> *cachedFirstNameMap
= (self.cachedFirstNameMap ? [self.cachedFirstNameMap mutableCopy] : [NSMutableDictionary new]);
NSMutableDictionary<NSString *, NSString *> *cachedLastNameMap
= (self.cachedLastNameMap ? [self.cachedLastNameMap mutableCopy] : [NSMutableDictionary new]);
for (SignalAccount *signalAccount in self.signalAccounts) {
NSString *baseName
= (signalAccount.contact.fullName.length > 0 ? signalAccount.contact.fullName : signalAccount.recipientId);
OWSAssert(signalAccount.hasMultipleAccountContact == (signalAccount.multipleAccountLabelText != nil));
NSString *displayName = (signalAccount.multipleAccountLabelText
? [NSString stringWithFormat:@"%@ (%@)", baseName, signalAccount.multipleAccountLabelText]
: baseName);
if (![displayName isEqualToString:signalAccount.recipientId]) {
cachedAccountNameMap[signalAccount.recipientId] = displayName;
}
if (signalAccount.contact.firstName.length > 0) {
cachedFirstNameMap[signalAccount.recipientId] = signalAccount.contact.firstName;
}
if (signalAccount.contact.lastName.length > 0) {
cachedLastNameMap[signalAccount.recipientId] = signalAccount.contact.lastName;
}
}
self.cachedAccountNameMap = [cachedAccountNameMap copy];
self.cachedFirstNameMap = [cachedFirstNameMap copy];
self.cachedLastNameMap = [cachedLastNameMap copy];
// Write to database off the main thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[TSStorageManager.sharedManager.newDatabaseConnection readWriteWithBlock:^(
YapDatabaseReadWriteTransaction *_Nonnull transaction) {
for (NSString *recipientId in cachedAccountNameMap) {
NSString *displayName = cachedAccountNameMap[recipientId];
[transaction setObject:displayName
forKey:recipientId
inCollection:kTSStorageManager_AccountDisplayNames];
}
for (NSString *recipientId in cachedFirstNameMap) {
NSString *firstName = cachedFirstNameMap[recipientId];
[transaction setObject:firstName forKey:recipientId inCollection:kTSStorageManager_AccountFirstNames];
}
for (NSString *recipientId in cachedLastNameMap) {
NSString *lastName = cachedLastNameMap[recipientId];
[transaction setObject:lastName forKey:recipientId inCollection:kTSStorageManager_AccountLastNames];
}
}];
});
}
- (void)loadCachedDisplayNames
{
// Read from database off the main thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary<NSString *, NSString *> *cachedAccountNameMap = [NSMutableDictionary new];
NSMutableDictionary<NSString *, NSString *> *cachedFirstNameMap = [NSMutableDictionary new];
NSMutableDictionary<NSString *, NSString *> *cachedLastNameMap = [NSMutableDictionary new];
[TSStorageManager.sharedManager.newDatabaseConnection readWithBlock:^(
YapDatabaseReadTransaction *_Nonnull transaction) {
[transaction
enumerateKeysAndObjectsInCollection:kTSStorageManager_AccountDisplayNames
usingBlock:^(
NSString *_Nonnull key, NSString *_Nonnull object, BOOL *_Nonnull stop) {
cachedAccountNameMap[key] = object;
}];
[transaction
enumerateKeysAndObjectsInCollection:kTSStorageManager_AccountFirstNames
usingBlock:^(
NSString *_Nonnull key, NSString *_Nonnull object, BOOL *_Nonnull stop) {
cachedFirstNameMap[key] = object;
}];
[transaction
enumerateKeysAndObjectsInCollection:kTSStorageManager_AccountLastNames
usingBlock:^(
NSString *_Nonnull key, NSString *_Nonnull object, BOOL *_Nonnull stop) {
cachedLastNameMap[key] = object;
}];
}];
if (self.cachedAccountNameMap || self.cachedFirstNameMap || self.cachedLastNameMap) {
// If these properties have already been populated from system contacts,
// don't overwrite. In practice this should never happen.
OWSAssert(0);
return;
}
self.cachedAccountNameMap = [cachedAccountNameMap copy];
self.cachedFirstNameMap = [cachedFirstNameMap copy];
self.cachedLastNameMap = [cachedLastNameMap copy];
});
}
- (NSString *_Nullable)cachedDisplayNameForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
return self.cachedAccountNameMap[recipientId];
}
- (NSString *_Nullable)cachedFirstNameForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
return self.cachedFirstNameMap[recipientId];
}
- (NSString *_Nullable)cachedLastNameForRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
return self.cachedLastNameMap[recipientId];
}
#pragma mark - View Helpers
// TODO move into Contact class.
+ (NSString *)accountLabelForContact:(Contact *)contact recipientId:(NSString *)recipientId
{
@ -245,27 +382,16 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
@"Displayed if for some reason we can't determine a contacts phone number *or* name");
}
- (NSString * _Nonnull)displayNameForPhoneIdentifier:(NSString * _Nullable)identifier {
if (!identifier) {
- (NSString *_Nonnull)displayNameForPhoneIdentifier:(NSString *_Nullable)recipientId
{
if (!recipientId) {
return self.unknownContactName;
}
// When viewing an old thread with someone who is no longer a Signal user, they won't have a SignalAccount
// so we get the name from `allContactsMap` as opposed to `signalAccountForRecipientId`.
Contact *contact = self.allContactsMap[identifier];
NSString *displayName = (contact.fullName.length > 0) ? contact.fullName : identifier;
return displayName;
}
// TODO move into Contact class.
- (NSString *_Nonnull)displayNameForContact:(Contact *)contact
{
OWSAssert(contact);
NSString *displayName = (contact.fullName.length > 0) ? contact.fullName : self.unknownContactName;
NSString *displayName = [self cachedDisplayNameForRecipientId:recipientId];
if (displayName.length < 1) {
displayName = recipientId;
}
return displayName;
}
@ -273,14 +399,7 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
{
OWSAssert(signalAccount);
NSString *baseName = (signalAccount.contact ? [self displayNameForContact:signalAccount.contact]
: [self displayNameForPhoneIdentifier:signalAccount.recipientId]);
OWSAssert(signalAccount.hasMultipleAccountContact == (signalAccount.multipleAccountLabelText != nil));
if (signalAccount.multipleAccountLabelText) {
return [NSString stringWithFormat:@"%@ (%@)", baseName, signalAccount.multipleAccountLabelText];
} else {
return baseName;
}
return [self displayNameForPhoneIdentifier:signalAccount.recipientId];
}
- (NSAttributedString *_Nonnull)formattedDisplayNameForSignalAccount:(SignalAccount *)signalAccount
@ -289,91 +408,75 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification =
OWSAssert(signalAccount);
OWSAssert(font);
NSAttributedString *baseName = [self formattedFullNameForContact:signalAccount.contact font:font];
if (baseName.length == 0) {
baseName = [self formattedFullNameForRecipientId:signalAccount.recipientId font:font];
}
OWSAssert(signalAccount.hasMultipleAccountContact == (signalAccount.multipleAccountLabelText != nil));
if (signalAccount.multipleAccountLabelText) {
NSMutableAttributedString *result = [NSMutableAttributedString new];
[result appendAttributedString:baseName];
[result appendAttributedString:[[NSAttributedString alloc] initWithString:@" ("
attributes:@{
NSFontAttributeName : font,
}]];
[result
appendAttributedString:[[NSAttributedString alloc] initWithString:signalAccount.multipleAccountLabelText]];
[result appendAttributedString:[[NSAttributedString alloc] initWithString:@")"
attributes:@{
NSFontAttributeName : font,
}]];
return result;
} else {
return baseName;
}
return [self formattedFullNameForRecipientId:signalAccount.recipientId font:font];
}
// TODO move into Contact class.
- (NSAttributedString *_Nonnull)formattedFullNameForContact:(Contact *)contact font:(UIFont *_Nonnull)font
- (NSAttributedString *)formattedFullNameForRecipientId:(NSString *)recipientId font:(UIFont *)font
{
OWSAssert(recipientId.length > 0);
OWSAssert(font);
UIFont *boldFont = [UIFont ows_mediumFontWithSize:font.pointSize];
NSDictionary<NSString *, id> *boldFontAttributes =
@{ NSFontAttributeName : boldFont, NSForegroundColorAttributeName : [UIColor blackColor] };
NSDictionary<NSString *, id> *normalFontAttributes =
@{ NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor ows_darkGrayColor] };
NSAttributedString *_Nullable firstName, *_Nullable lastName;
if (ABPersonGetSortOrdering() == kABPersonSortByFirstName) {
if (contact.firstName) {
firstName = [[NSAttributedString alloc] initWithString:contact.firstName attributes:boldFontAttributes];
}
if (contact.lastName) {
lastName = [[NSAttributedString alloc] initWithString:contact.lastName attributes:normalFontAttributes];
}
} else {
if (contact.firstName) {
firstName = [[NSAttributedString alloc] initWithString:contact.firstName attributes:normalFontAttributes];
NSDictionary<NSString *, id> *firstNameAttributes
= (ABPersonGetSortOrdering() == kABPersonSortByFirstName ? boldFontAttributes : normalFontAttributes);
NSDictionary<NSString *, id> *lastNameAttributes
= (ABPersonGetSortOrdering() == kABPersonSortByFirstName ? normalFontAttributes : boldFontAttributes);
NSString *cachedFirstName = [self cachedFirstNameForRecipientId:recipientId];
NSString *cachedLastName = [self cachedLastNameForRecipientId:recipientId];
NSMutableAttributedString *formattedName = [NSMutableAttributedString new];
if (cachedFirstName.length > 0 && cachedLastName.length > 0) {
NSAttributedString *firstName =
[[NSAttributedString alloc] initWithString:cachedFirstName attributes:firstNameAttributes];
NSAttributedString *lastName =
[[NSAttributedString alloc] initWithString:cachedLastName attributes:lastNameAttributes];
NSAttributedString *_Nullable leftName, *_Nullable rightName;
if (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst) {
leftName = firstName;
rightName = lastName;
} else {
leftName = lastName;
rightName = firstName;
}
if (contact.lastName) {
lastName = [[NSAttributedString alloc] initWithString:contact.lastName attributes:boldFontAttributes];
}
}
NSAttributedString *_Nullable leftName, *_Nullable rightName;
if (ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst) {
leftName = firstName;
rightName = lastName;
[formattedName appendAttributedString:leftName];
[formattedName
appendAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:normalFontAttributes]];
[formattedName appendAttributedString:rightName];
} else if (cachedFirstName.length > 0) {
[formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:cachedFirstName
attributes:firstNameAttributes]];
} else if (cachedLastName.length > 0) {
[formattedName appendAttributedString:[[NSAttributedString alloc] initWithString:cachedLastName
attributes:lastNameAttributes]];
} else {
leftName = lastName;
rightName = firstName;
return [[NSAttributedString alloc]
initWithString:[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId]
attributes:normalFontAttributes];
}
NSMutableAttributedString *fullNameString = [NSMutableAttributedString new];
if (leftName.length > 0) {
[fullNameString appendAttributedString:leftName];
}
if (leftName.length > 0 && rightName.length > 0) {
[fullNameString appendAttributedString:[[NSAttributedString alloc] initWithString:@" "]];
SignalAccount *signalAccount = [self signalAccountForRecipientId:recipientId];
if (signalAccount && signalAccount.multipleAccountLabelText) {
OWSAssert(signalAccount.multipleAccountLabelText.length > 0);
[formattedName
appendAttributedString:[[NSAttributedString alloc] initWithString:@" (" attributes:normalFontAttributes]];
[formattedName
appendAttributedString:[[NSAttributedString alloc] initWithString:signalAccount.multipleAccountLabelText
attributes:normalFontAttributes]];
[formattedName
appendAttributedString:[[NSAttributedString alloc] initWithString:@")" attributes:normalFontAttributes]];
}
if (rightName.length > 0) {
[fullNameString appendAttributedString:rightName];
}
return fullNameString;
}
- (NSAttributedString *)formattedFullNameForRecipientId:(NSString *)recipientId font:(UIFont *)font
{
NSDictionary<NSString *, id> *normalFontAttributes =
@{ NSFontAttributeName : font, NSForegroundColorAttributeName : [UIColor ows_darkGrayColor] };
return [[NSAttributedString alloc]
initWithString:[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:recipientId]
attributes:normalFontAttributes];
return formattedName;
}
- (nullable SignalAccount *)signalAccountForRecipientId:(NSString *)recipientId

@ -146,7 +146,6 @@
}
- (void)notifyUserForIncomingMessage:(TSIncomingMessage *)message
from:(NSString *)name
inThread:(TSThread *)thread
contactsManager:(id<ContactsManagerProtocol>)contactsManager
{
@ -155,6 +154,11 @@
}
NSString *messageDescription = message.description;
NSString *senderName = [contactsManager displayNameForPhoneIdentifier:message.authorId];
NSString *groupName = [thread.name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (groupName.length < 1) {
groupName = NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"");
}
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive && messageDescription) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
@ -167,25 +171,25 @@
@{Signal_Thread_UserInfo_Key : thread.uniqueId, Signal_Message_UserInfo_Key : message.uniqueId};
if ([thread isGroupThread]) {
NSString *sender = [contactsManager displayNameForPhoneIdentifier:message.authorId];
NSString *threadName = [NSString stringWithFormat:@"\"%@\"", name];
NSString *threadName = [NSString stringWithFormat:@"\"%@\"", groupName];
// TODO: Format parameters might change order in l10n. We should use named parameters.
notification.alertBody =
[NSString stringWithFormat:NSLocalizedString(@"APN_MESSAGE_IN_GROUP_DETAILED", nil),
sender,
threadName,
messageDescription];
senderName,
threadName,
messageDescription];
} else {
notification.alertBody = [NSString stringWithFormat:@"%@: %@", name, messageDescription];
notification.alertBody = [NSString stringWithFormat:@"%@: %@", senderName, messageDescription];
}
break;
case NotificationNameNoPreview: {
notification.userInfo = @{Signal_Thread_UserInfo_Key : thread.uniqueId};
if ([thread isGroupThread]) {
notification.alertBody =
[NSString stringWithFormat:@"%@ \"%@\"", NSLocalizedString(@"APN_MESSAGE_IN_GROUP", nil), name];
notification.alertBody = [NSString
stringWithFormat:@"%@ \"%@\"", NSLocalizedString(@"APN_MESSAGE_IN_GROUP", nil), groupName];
} else {
notification.alertBody =
[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"APN_MESSAGE_FROM", nil), name];
[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"APN_MESSAGE_FROM", nil), senderName];
}
break;
}

Loading…
Cancel
Save