diff --git a/Signal/src/ViewControllers/ContactShareViewHelper.swift b/Signal/src/ViewControllers/ContactShareViewHelper.swift index 436e9b63c..89e6c4d74 100644 --- a/Signal/src/ViewControllers/ContactShareViewHelper.swift +++ b/Signal/src/ViewControllers/ContactShareViewHelper.swift @@ -144,7 +144,7 @@ public class ContactShareViewHelper: NSObject, CNContactViewControllerDelegate { return } - guard let systemContact = OWSContacts.systemContact(for: contactShare.dbRecord) else { + guard let systemContact = OWSContacts.systemContact(for: contactShare.dbRecord, imageData: contactShare.avatarImageData) else { owsFail("\(logTag) Could not derive system contact.") return } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index c976699fe..8f05412e5 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -5004,20 +5004,20 @@ interactionControllerForAnimationController:(id CNContact? { - guard let newCNContact = OWSContacts.systemContact(for: self.dbRecord) else { + guard let newCNContact = OWSContacts.systemContact(for: self.dbRecord, imageData: self.avatarImageData) else { owsFail("\(logTag) in \(#function) newCNContact was unexpectedly nil") return nil } @@ -128,7 +136,7 @@ public class ContactShareViewModel: NSObject { // TODO move the `copy` logic into the view model? let newDbRecord = dbRecord.copy(withNamePrefix: namePrefix, givenName: givenName, middleName: middleName, familyName: familyName, nameSuffix: nameSuffix) - return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImage: self.avatarImage) + return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImageData: self.avatarImageData) } public func newContact(withNamePrefix namePrefix: String?, @@ -144,7 +152,7 @@ public class ContactShareViewModel: NSObject { familyName: familyName, nameSuffix: nameSuffix) - return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImage: self.avatarImage) + return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImageData: self.avatarImageData) } } diff --git a/SignalMessaging/attachments/SharingThreadPickerViewController.m b/SignalMessaging/attachments/SharingThreadPickerViewController.m index 2ae811df9..15e47f0a6 100644 --- a/SignalMessaging/attachments/SharingThreadPickerViewController.m +++ b/SignalMessaging/attachments/SharingThreadPickerViewController.m @@ -159,11 +159,11 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion); // TODO: Populate avatar image. BOOL isProfileAvatar = NO; - UIImage *_Nullable avatarImage = nil; + NSData *_Nullable avatarImageData = nil; contact.isProfileAvatar = isProfileAvatar; ContactShareViewModel *contactShare = - [[ContactShareViewModel alloc] initWithContactShareRecord:contact avatarImage:avatarImage]; + [[ContactShareViewModel alloc] initWithContactShareRecord:contact avatarImageData:avatarImageData]; ApproveContactShareViewController *approvalVC = [[ApproveContactShareViewController alloc] initWithContactShare:contactShare diff --git a/SignalMessaging/contacts/OWSContactsManager.h b/SignalMessaging/contacts/OWSContactsManager.h index 92ce39844..f49dc29cb 100644 --- a/SignalMessaging/contacts/OWSContactsManager.h +++ b/SignalMessaging/contacts/OWSContactsManager.h @@ -81,6 +81,7 @@ extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification; - (nullable UIImage *)systemContactImageForPhoneIdentifier:(nullable NSString *)identifier; - (nullable UIImage *)profileImageForPhoneIdentifier:(nullable NSString *)identifier; +- (nullable NSData *)profileImageDataForPhoneIdentifier:(nullable NSString *)identifier; - (nullable UIImage *)imageForPhoneIdentifier:(nullable NSString *)identifier; - (NSAttributedString *)formattedDisplayNameForSignalAccount:(SignalAccount *)signalAccount font:(UIFont *_Nonnull)font; diff --git a/SignalMessaging/contacts/OWSContactsManager.m b/SignalMessaging/contacts/OWSContactsManager.m index bde6d537f..9e0f07a58 100644 --- a/SignalMessaging/contacts/OWSContactsManager.m +++ b/SignalMessaging/contacts/OWSContactsManager.m @@ -739,6 +739,11 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification return [self.profileManager profileAvatarForRecipientId:identifier]; } +- (nullable NSData *)profileImageDataForPhoneIdentifier:(nullable NSString *)identifier +{ + return [self.profileManager profileAvatarDataForRecipientId:identifier]; +} + - (UIImage *_Nullable)imageForPhoneIdentifier:(NSString *_Nullable)identifier { // Prefer the contact image from the local address book if available diff --git a/SignalMessaging/profiles/OWSProfileManager.h b/SignalMessaging/profiles/OWSProfileManager.h index 983363b59..d1b228d98 100644 --- a/SignalMessaging/profiles/OWSProfileManager.h +++ b/SignalMessaging/profiles/OWSProfileManager.h @@ -75,6 +75,7 @@ extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter; - (nullable NSString *)profileNameForRecipientId:(NSString *)recipientId; - (nullable UIImage *)profileAvatarForRecipientId:(NSString *)recipientId; +- (nullable NSData *)profileAvatarDataForRecipientId:(NSString *)recipientId; - (void)updateProfileForRecipientId:(NSString *)recipientId profileNameEncrypted:(nullable NSData *)profileNameEncrypted diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index 796d9b3ed..3f3a2822e 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -783,6 +783,20 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640; return nil; } +- (nullable NSData *)profileAvatarDataForRecipientId:(NSString *)recipientId +{ + OWSAssert(recipientId.length > 0); + + OWSUserProfile *userProfile = + [OWSUserProfile getOrBuildUserProfileForRecipientId:recipientId dbConnection:self.dbConnection]; + + if (userProfile.avatarFileName.length > 0) { + return [self loadProfileDataWithFilename:userProfile.avatarFileName]; + } + + return nil; +} + - (void)downloadAvatarForUserProfile:(OWSUserProfile *)userProfile { OWSAssert(userProfile); diff --git a/SignalServiceKit/src/Contacts/Contact.h b/SignalServiceKit/src/Contacts/Contact.h index 20ce68c66..aec8c014b 100644 --- a/SignalServiceKit/src/Contacts/Contact.h +++ b/SignalServiceKit/src/Contacts/Contact.h @@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL isSignalContact; #if TARGET_OS_IOS @property (nullable, readonly, nonatomic) UIImage *image; +@property (nullable, readonly, nonatomic) NSData *imageData; @property (nullable, nonatomic, readonly) CNContact *cnContact; #endif // TARGET_OS_IOS diff --git a/SignalServiceKit/src/Contacts/Contact.m b/SignalServiceKit/src/Contacts/Contact.m index 01055d81b..84d82b9a8 100644 --- a/SignalServiceKit/src/Contacts/Contact.m +++ b/SignalServiceKit/src/Contacts/Contact.m @@ -17,7 +17,6 @@ NS_ASSUME_NONNULL_BEGIN @interface Contact () @property (readonly, nonatomic) NSMutableDictionary *phoneNumberNameMap; -@property (readonly, nonatomic) NSData *imageData; @end @@ -336,14 +335,15 @@ NS_ASSUME_NONNULL_BEGIN // Address // merged all or nothing - do not try to piece-meal merge. - BOOL hasExistingAddress = NO; - for (CNLabeledValue *labeledPostalAddress in mergedCNContact.postalAddresses) { - hasExistingAddress = YES; - } - if (!hasExistingAddress) { + if (mergedCNContact.postalAddresses.count == 0) { mergedCNContact.postalAddresses = newCNContact.postalAddresses; } + // Avatar + if (!mergedCNContact.imageData) { + mergedCNContact.imageData = newCNContact.imageData; + } + return [mergedCNContact copy]; } diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h index 8657739a8..a14f48801 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h @@ -40,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable UIImage *)image; - (nullable UIImage *)thumbnailImage; - (nullable NSData *)thumbnailData; +- (nullable NSData *)validStillImageData; #endif - (BOOL)isAnimated; diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index c5b523a38..a7d19a17c 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -331,6 +331,30 @@ NS_ASSUME_NONNULL_BEGIN } } +- (nullable NSData *)validStillImageData +{ + if ([self isVideo]) { + OWSFail(@"%@ in %s isVideo was unexpectedly true", self.logTag, __PRETTY_FUNCTION__); + return nil; + } + if ([self isAnimated]) { + OWSFail(@"%@ in %s isAnimated was unexpectedly true", self.logTag, __PRETTY_FUNCTION__); + return nil; + } + + NSURL *_Nullable mediaUrl = [self mediaURL]; + if (!mediaUrl) { + return nil; + } + + NSData *data = [NSData dataWithContentsOfURL:mediaUrl]; + if (![data ows_isValidImage]) { + return nil; + } + + return data; +} + + (BOOL)hasThumbnailForMimeType:(NSString *)contentType { return ([MIMETypeUtil isVideo:contentType] || [MIMETypeUtil isImage:contentType] || diff --git a/SignalServiceKit/src/Messages/Interactions/OWSContact.h b/SignalServiceKit/src/Messages/Interactions/OWSContact.h index 91058668a..8afad46cd 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSContact.h +++ b/SignalServiceKit/src/Messages/Interactions/OWSContact.h @@ -166,7 +166,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value); #pragma mark - System Contact Conversion + (nullable OWSContact *)contactForSystemContact:(CNContact *)systemContact; -+ (nullable CNContact *)systemContactForContact:(OWSContact *)contact; ++ (nullable CNContact *)systemContactForContact:(OWSContact *)contact imageData:(nullable NSData *)imageData; #pragma mark - diff --git a/SignalServiceKit/src/Messages/Interactions/OWSContact.m b/SignalServiceKit/src/Messages/Interactions/OWSContact.m index 935868841..975db2145 100644 --- a/SignalServiceKit/src/Messages/Interactions/OWSContact.m +++ b/SignalServiceKit/src/Messages/Interactions/OWSContact.m @@ -372,7 +372,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value) - (void)ensureDisplayName { if (_displayName.length < 1) { - CNContact *_Nullable cnContact = [OWSContacts systemContactForContact:self]; + CNContact *_Nullable cnContact = [OWSContacts systemContactForContact:self imageData:nil]; _displayName = [Contact formattedFullNameWithCNContact:cnContact]; } if (_displayName.length < 1) { @@ -693,7 +693,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value) return contact; } -+ (nullable CNContact *)systemContactForContact:(OWSContact *)contact ++ (nullable CNContact *)systemContactForContact:(OWSContact *)contact imageData:(nullable NSData *)imageData { if (!contact) { OWSProdLogAndFail(@"%@ Missing contact.", self.logTag); @@ -790,11 +790,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value) } } systemContact.postalAddresses = systemAddresses; - - // TODO: Avatar - - // @property (readonly, copy, nullable, NS_NONATOMIC_IOSONLY) NSData *imageData; - // @property (readonly, copy, nullable, NS_NONATOMIC_IOSONLY) NSData *thumbnailImageData; + systemContact.imageData = imageData; return systemContact; } @@ -816,7 +812,8 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value) { OWSAssert(contact); - CNContact *_Nullable systemContact = [self systemContactForContact:contact]; + // TODO pass in image for vcard + CNContact *_Nullable systemContact = [self systemContactForContact:contact imageData:nil]; if (!systemContact) { return nil; }