Less confusing "#" avatar for unknown Contact instead of "+"

For consistency with the Android and Desktop client behavior.

* Show a placeholder avatar when no image, initials (#1512)

  If all we know about the user is their phone number, their avatar image
  is rendered as a placeholder. Previously, it would render the first few
  characters of their phone number as if they were initials (eg. "+")

* Rename, extend OWSContactsManager methods (#1512)

  Rename from: nameStringForPhoneIdentifier
           to: displayNameForPhoneIdentifier

  Also, add:
  - (BOOL)nameExistsForPhoneIdentifier:(NSString *)identifier;

  Which reports whether there's any "name" for a contact.

* Remove unused typedefs

  These aren't used in the project anymore, and they were causing
  compiling warnings due to a lack of nullability indication.

* Resolve some OWSContactsManager nullability warnings

  Did a pass through all of the existing nullability warnings in
  OWSContactsManager. Tried to pick descriptors that best reflected the
  behavior of the methods.

// FREEBIE
This commit is contained in:
Russ Shanahan 2016-12-01 16:17:57 -05:00 committed by Michael Kirk
parent fb508470d9
commit e7126f8c60
8 changed files with 64 additions and 43 deletions

View File

@ -132,7 +132,7 @@ EXTERNAL SOURCES:
CHECKOUT OPTIONS:
SignalServiceKit:
:commit: 34ffce89f59356ab23f290866b1c3437f03312ce
:commit: 71250281596cdd6a03072d1b4a23aea8ce490eeb
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:commit: 41b57bb2fc292a814f758441a05243eb38457027

View File

@ -57,8 +57,9 @@ NS_ASSUME_NONNULL_BEGIN
}
NSMutableString *initials = [NSMutableString string];
if (self.contactName.length > 0) {
BOOL contactHasName = [self.contactsManager nameExistsForPhoneIdentifier:self.signalId];
if (contactHasName) {
// Make an image from the contact's initials
NSArray *words =
[self.contactName componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
for (NSString *word in words) {
@ -67,19 +68,20 @@ NS_ASSUME_NONNULL_BEGIN
[initials appendString:[firstLetter uppercaseString]];
}
}
NSRange stringRange = { 0, MIN([initials length], (NSUInteger)3) }; // Rendering max 3 letters.
initials = [[initials substringWithRange:stringRange] mutableCopy];
} else {
// We don't have a name for this contact, so we can't make an "initials" image
[initials appendString:@"#"];
}
NSRange stringRange = { 0, MIN([initials length], (NSUInteger)3) }; // Rendering max 3 letters.
initials = [[initials substringWithRange:stringRange] mutableCopy];
UIColor *backgroundColor = [UIColor backgroundColorForContact:self.signalId];
UIImage *image = [[JSQMessagesAvatarImageFactory avatarImageWithUserInitials:initials
backgroundColor:backgroundColor
textColor:[UIColor whiteColor]
font:[UIFont ows_boldFontWithSize:36.0]
diameter:100] avatarImage];
[self.contactsManager.avatarCache setObject:image forKey:self.signalId];
return image;
}

View File

@ -100,7 +100,7 @@
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
NSString *contactId = ((TSContactThread *)thread).contactIdentifier;
adapter.senderId = contactId;
adapter.senderDisplayName = [contactsManager nameStringForPhoneIdentifier:contactId];
adapter.senderDisplayName = [contactsManager displayNameForPhoneIdentifier:contactId];
adapter.messageType = TSIncomingMessageAdapter;
} else {
adapter.senderId = ME_MESSAGE_IDENTIFIER;
@ -111,7 +111,7 @@
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *message = (TSIncomingMessage *)interaction;
adapter.senderId = message.authorId;
adapter.senderDisplayName = [contactsManager nameStringForPhoneIdentifier:message.authorId];
adapter.senderDisplayName = [contactsManager displayNameForPhoneIdentifier:message.authorId];
adapter.messageType = TSIncomingMessageAdapter;
} else {
adapter.senderId = ME_MESSAGE_IDENTIFIER;

View File

@ -12,28 +12,26 @@
#define SIGNAL_LIST_UPDATED @"Signal_AB_UPDATED"
typedef void (^ABAccessRequestCompletionBlock)(BOOL hasAccess);
typedef void (^ABReloadRequestCompletionBlock)(NSArray *contacts);
@interface OWSContactsManager : NSObject <ContactsManagerProtocol>
@property CNContactStore *contactStore;
@property NSCache<NSString *, UIImage *> *avatarCache;
@property CNContactStore * _Nullable contactStore;
@property NSCache<NSString *, UIImage *> * _Nonnull avatarCache;
- (ObservableValue *)getObservableContacts;
- (ObservableValue * _Nonnull)getObservableContacts;
- (NSArray *)getContactsFromAddressBook:(ABAddressBookRef)addressBook;
- (Contact *)latestContactForPhoneNumber:(PhoneNumber *)phoneNumber;
- (NSArray * _Nonnull)getContactsFromAddressBook:(ABAddressBookRef _Nonnull)addressBook;
- (Contact * _Nullable)latestContactForPhoneNumber:(PhoneNumber * _Nullable)phoneNumber;
- (void)verifyABPermission;
- (NSArray<Contact *> *)allContacts;
- (NSArray<Contact *> *)signalContacts;
- (NSArray<Contact *> * _Nonnull)allContacts;
- (NSArray<Contact *> * _Nonnull)signalContacts;
- (void)doAfterEnvironmentInitSetup;
- (NSString *)nameStringForPhoneIdentifier:(NSString *)identifier;
- (UIImage *)imageForPhoneIdentifier:(NSString *)identifier;
- (NSString * _Nonnull)displayNameForPhoneIdentifier:(NSString * _Nullable)identifier;
- (BOOL)nameExistsForPhoneIdentifier:(NSString * _Nullable)identifier;
- (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)identifier;
+ (NSComparator)contactComparator;

View File

@ -224,7 +224,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
return futureAddressBookSource.future;
}
- (NSArray *)getContactsFromAddressBook:(ABAddressBookRef)addressBook {
- (NSArray *)getContactsFromAddressBook:(ABAddressBookRef _Nonnull)addressBook {
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFMutableArrayRef allPeopleMutable =
CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(allPeople), allPeople);
@ -290,7 +290,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
andContactID:recordID];
}
- (Contact *)latestContactForPhoneNumber:(PhoneNumber *)phoneNumber {
- (Contact * _Nullable)latestContactForPhoneNumber:(PhoneNumber *)phoneNumber {
NSArray *allContacts = [self allContacts];
ContactSearchBlock searchBlock = ^BOOL(Contact *contact, NSUInteger idx, BOOL *stop) {
@ -361,7 +361,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
}
+ (BOOL)name:(NSString *)nameString matchesQuery:(NSString *)queryString {
+ (BOOL)name:(NSString * _Nonnull)nameString matchesQuery:(NSString * _Nonnull)queryString {
NSCharacterSet *whitespaceSet = NSCharacterSet.whitespaceCharacterSet;
NSArray *queryStrings = [queryString componentsSeparatedByCharactersInSet:whitespaceSet];
NSArray *nameStrings = [nameString componentsSeparatedByCharactersInSet:whitespaceSet];
@ -395,36 +395,57 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
return [Contact comparatorSortingNamesByFirstThenLast:firstNameOrdering];
}
- (NSArray<Contact *> *)signalContacts {
- (NSArray<Contact *> * _Nonnull)signalContacts {
return [self getSignalUsersFromContactsArray:[self allContacts]];
}
- (NSString *)nameStringForPhoneIdentifier:(NSString *)identifier {
- (NSString * _Nonnull)displayNameForPhoneIdentifier:(NSString * _Nullable)identifier {
if (!identifier) {
return NSLocalizedString(@"UNKNOWN_CONTACT_NAME",
@"Displayed if for some reason we can't determine a contacts phone number *or* name");
}
for (Contact *contact in self.allContacts) {
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
if ([phoneNumber.toE164 isEqualToString:identifier]) {
return contact.fullName;
}
}
}
return identifier;
Contact *contact = [self contactForPhoneIdentifier:identifier];
NSString *displayName = (contact.fullName.length > 0) ? contact.fullName : identifier;
return displayName;
}
- (UIImage *)imageForPhoneIdentifier:(NSString *)identifier {
- (BOOL)nameExistsForPhoneIdentifier:(NSString * _Nullable)identifier {
Contact *contact = [self contactForPhoneIdentifier:identifier];
NSString *name = contact.fullName;
if (name.length <= 0) return NO;
// OWSContactsManager::contactForRecord will use the first phone number as a name
// in absense of a name or business name during import. Make sure that's not happening here.
if ((contact.userTextPhoneNumbers.count > 0) && ([contact.userTextPhoneNumbers[0] isEqualToString:name])) {
return NO;
}
return YES;
}
- (Contact * _Nullable)contactForPhoneIdentifier:(NSString * _Nullable)identifier {
if (!identifier) {
return nil;
}
for (Contact *contact in self.allContacts) {
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
if ([phoneNumber.toE164 isEqualToString:identifier]) {
return contact.image;
return contact;
}
}
}
return nil;
}
- (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)identifier {
Contact *contact = [self contactForPhoneIdentifier:identifier];
return contact.image;
}
#pragma mark - Logging
+ (NSString *)tag

View File

@ -113,7 +113,7 @@
@{Signal_Thread_UserInfo_Key : thread.uniqueId, Signal_Message_UserInfo_Key : message.uniqueId};
if ([thread isGroupThread]) {
NSString *sender = [self.contactsManager nameStringForPhoneIdentifier:message.authorId];
NSString *sender = [self.contactsManager displayNameForPhoneIdentifier:message.authorId];
NSString *threadName = [NSString stringWithFormat:@"\"%@\"", name];
notification.alertBody =
[NSString stringWithFormat:NSLocalizedString(@"APN_MESSAGE_IN_GROUP_DETAILED", nil),

View File

@ -111,7 +111,7 @@
UILocalNotification *notification = [[UILocalNotification alloc] init];
NSString *callerId = call.initiatorNumber.toE164;
NSString *displayName = [self.contactsManager nameStringForPhoneIdentifier:callerId];
NSString *displayName = [self.contactsManager displayNameForPhoneIdentifier:callerId];
PropertyListPreferences *prefs = [Environment preferences];
notification.alertBody = @"☎️ ";

View File

@ -1079,7 +1079,7 @@ typedef enum : NSUInteger {
}
} else if (message.messageType == TSIncomingMessageAdapter && [self.thread isKindOfClass:[TSGroupThread class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message.interaction;
NSString *_Nonnull name = [self.contactsManager nameStringForPhoneIdentifier:incomingMessage.authorId];
NSString *_Nonnull name = [self.contactsManager displayNameForPhoneIdentifier:incomingMessage.authorId];
NSAttributedString *senderNameString = [[NSAttributedString alloc] initWithString:name];
return senderNameString;
@ -1539,7 +1539,7 @@ typedef enum : NSUInteger {
- (void)tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)errorMessage
{
NSString *keyOwner = [self.contactsManager nameStringForPhoneIdentifier:errorMessage.theirSignalId];
NSString *keyOwner = [self.contactsManager displayNameForPhoneIdentifier:errorMessage.theirSignalId];
NSString *titleFormat = NSLocalizedString(@"SAFETY_NUMBERS_ACTIONSHEET_TITLE", @"Action sheet heading");
NSString *titleText = [NSString stringWithFormat:titleFormat, keyOwner];
@ -1601,7 +1601,7 @@ typedef enum : NSUInteger {
}
OWSFingerprint *fingerprint = (OWSFingerprint *)sender;
NSString *contactName = [self.contactsManager nameStringForPhoneIdentifier:fingerprint.theirStableId];
NSString *contactName = [self.contactsManager displayNameForPhoneIdentifier:fingerprint.theirStableId];
[vc configureWithThread:self.thread fingerprint:fingerprint contactName:contactName];
} else if ([segue.destinationViewController isKindOfClass:[OWSConversationSettingsTableViewController class]]) {
OWSConversationSettingsTableViewController *controller