mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
8.5 KiB
Objective-C
228 lines
8.5 KiB
Objective-C
//
|
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
#import "OWSContactAvatarBuilder.h"
|
|
#import "OWSContactsManager.h"
|
|
#import "TSContactThread.h"
|
|
#import "TSGroupThread.h"
|
|
#import "TSThread.h"
|
|
#import "UIColor+OWS.h"
|
|
#import "UIFont+OWS.h"
|
|
#import <SignalMessaging/SignalMessaging-Swift.h>
|
|
#import <SessionServiceKit/SSKEnvironment.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
@interface OWSContactAvatarBuilder ()
|
|
|
|
@property (nonatomic, readonly) NSString *signalId;
|
|
@property (nonatomic, readonly) NSString *contactName;
|
|
@property (nonatomic, readonly) ConversationColorName colorName;
|
|
@property (nonatomic, readonly) NSUInteger diameter;
|
|
|
|
@end
|
|
|
|
@implementation OWSContactAvatarBuilder
|
|
|
|
#pragma mark - Initializers
|
|
|
|
- (instancetype)initWithContactId:(NSString *)contactId
|
|
name:(NSString *)name
|
|
colorName:(ConversationColorName)colorName
|
|
diameter:(NSUInteger)diameter
|
|
{
|
|
self = [super init];
|
|
if (!self) {
|
|
return self;
|
|
}
|
|
|
|
OWSAssertDebug(colorName.length > 0);
|
|
|
|
_signalId = contactId;
|
|
_contactName = name;
|
|
_colorName = colorName;
|
|
_diameter = diameter;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithSignalId:(NSString *)signalId
|
|
colorName:(ConversationColorName)colorName
|
|
diameter:(NSUInteger)diameter
|
|
{
|
|
// Name for avatar initials.
|
|
NSString *_Nullable name = [OWSContactAvatarBuilder.contactsManager nameFromSystemContactsForRecipientId:signalId];
|
|
if (name.length == 0) {
|
|
name = [OWSContactAvatarBuilder.contactsManager profileNameForRecipientId:signalId];
|
|
}
|
|
if (name.length == 0) {
|
|
name = signalId;
|
|
}
|
|
return [self initWithContactId:signalId name:name colorName:colorName diameter:diameter];
|
|
}
|
|
|
|
- (instancetype)initWithNonSignalName:(NSString *)nonSignalName
|
|
colorSeed:(NSString *)colorSeed
|
|
diameter:(NSUInteger)diameter
|
|
{
|
|
ConversationColorName colorName = [TSThread stableColorNameForNewConversationWithString:colorSeed];
|
|
return [self initWithContactId:colorSeed name:nonSignalName colorName:(NSString *)colorName diameter:diameter];
|
|
}
|
|
|
|
- (instancetype)initForLocalUserWithDiameter:(NSUInteger)diameter
|
|
{
|
|
NSString *localNumber = [NSUserDefaults.standardUserDefaults stringForKey:@"masterDeviceHexEncodedPublicKey"];
|
|
if (localNumber == nil) {
|
|
localNumber = [TSAccountManager localNumber];
|
|
}
|
|
OWSAssertDebug(localNumber.length > 0);
|
|
OWSAssertDebug(diameter > 0);
|
|
|
|
return [self initWithSignalId:localNumber colorName:kConversationColorName_Default diameter:diameter];
|
|
}
|
|
|
|
#pragma mark - Dependencies
|
|
|
|
+ (OWSContactsManager *)contactsManager
|
|
{
|
|
return (OWSContactsManager *)SSKEnvironment.shared.contactsManager;
|
|
}
|
|
|
|
#pragma mark - Instance methods
|
|
|
|
- (nullable UIImage *)buildSavedImage
|
|
{
|
|
NSString *masterDeviceHexEncodedPublicKey = [NSUserDefaults.standardUserDefaults stringForKey:@"masterDeviceHexEncodedPublicKey"];
|
|
if ([self.signalId isEqualToString:TSAccountManager.localNumber] || (masterDeviceHexEncodedPublicKey != nil && [self.signalId isEqualToString:masterDeviceHexEncodedPublicKey])) {
|
|
NSString *noteToSelfCacheKey = [NSString stringWithFormat:@"%@:note-to-self", self.cacheKey];
|
|
UIImage *_Nullable cachedAvatar =
|
|
[OWSContactAvatarBuilder.contactsManager.avatarCache imageForKey:noteToSelfCacheKey
|
|
diameter:(CGFloat)self.diameter];
|
|
if (cachedAvatar) {
|
|
return cachedAvatar;
|
|
}
|
|
|
|
UIImage *image = [self noteToSelfImageWithConversationColorName:self.colorName];
|
|
if (!image) {
|
|
OWSFailDebug(@"Could not generate avatar.");
|
|
return nil;
|
|
}
|
|
|
|
[OWSContactAvatarBuilder.contactsManager.avatarCache setImage:image
|
|
forKey:noteToSelfCacheKey
|
|
diameter:self.diameter];
|
|
return image;
|
|
}
|
|
|
|
return [OWSContactAvatarBuilder.contactsManager imageForPhoneIdentifier:self.signalId];
|
|
}
|
|
|
|
- (id)cacheKey
|
|
{
|
|
return [NSString stringWithFormat:@"%@-%d", self.signalId, Theme.isDarkThemeEnabled];
|
|
}
|
|
|
|
- (nullable UIImage *)buildDefaultImage
|
|
{
|
|
UIImage *_Nullable cachedAvatar =
|
|
[OWSContactAvatarBuilder.contactsManager.avatarCache imageForKey:self.cacheKey diameter:(CGFloat)self.diameter];
|
|
if (cachedAvatar) {
|
|
return cachedAvatar;
|
|
}
|
|
|
|
/**
|
|
NSMutableString *initials = [NSMutableString string];
|
|
|
|
NSRange rangeOfLetters = [self.contactName rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]];
|
|
if (rangeOfLetters.location != NSNotFound) {
|
|
// Contact name contains letters, so it's probably not just a phone number.
|
|
// Make an image from the contact's initials
|
|
NSCharacterSet *excludeAlphanumeric = [NSCharacterSet alphanumericCharacterSet].invertedSet;
|
|
NSArray *words =
|
|
[self.contactName componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
|
for (NSString *word in words) {
|
|
NSString *trimmedWord = [word stringByTrimmingCharactersInSet:excludeAlphanumeric];
|
|
if (trimmedWord.length > 0) {
|
|
NSString *firstLetter = [trimmedWord substringToIndex:1];
|
|
[initials appendString:firstLetter.localizedUppercaseString];
|
|
}
|
|
}
|
|
|
|
NSRange stringRange = { 0, MIN([initials length], (NSUInteger)3) }; // Rendering max 3 letters.
|
|
initials = [[initials substringWithRange:stringRange] mutableCopy];
|
|
}
|
|
|
|
UIColor *color = [OWSConversationColor conversationColorOrDefaultForColorName:self.colorName].themeColor;
|
|
OWSAssertDebug(color);
|
|
|
|
UIImage *_Nullable image;
|
|
if (initials.length == 0) {
|
|
// We don't have a name for this contact, so we can't make an "initials" image.
|
|
|
|
UIImage *icon;
|
|
if (self.diameter > kStandardAvatarSize) {
|
|
icon = [UIImage imageNamed:@"contact-avatar-1024"];
|
|
} else {
|
|
icon = [UIImage imageNamed:@"contact-avatar-84"];
|
|
}
|
|
CGFloat assetWidthPixels = CGImageGetWidth(icon.CGImage);
|
|
// The contact-avatar asset is designed to be 28pt if the avatar is kStandardAvatarSize.
|
|
// Adjust its size to reflect the actual output diameter.
|
|
// We use an oversize 1024px version of the asset to ensure quality results for larger avatars.
|
|
CGFloat scaling = (self.diameter / (CGFloat)kStandardAvatarSize) * (28 / assetWidthPixels);
|
|
|
|
CGSize iconSize = CGSizeScale(icon.size, scaling);
|
|
image =
|
|
[OWSAvatarBuilder avatarImageWithIcon:icon iconSize:iconSize backgroundColor:color diameter:self.diameter];
|
|
} else {
|
|
image = [OWSAvatarBuilder avatarImageWithInitials:initials backgroundColor:color diameter:self.diameter];
|
|
}
|
|
|
|
if (!image) {
|
|
OWSFailDebug(@"Could not generate avatar.");
|
|
return nil;
|
|
}
|
|
*/
|
|
|
|
UIImage *image = [LKIdenticon generatePlaceholderIconWithSeed:self.signalId text:@"0" size:((CGFloat)self.diameter)];
|
|
[OWSContactAvatarBuilder.contactsManager.avatarCache setImage:image forKey:self.cacheKey diameter:self.diameter];
|
|
return image;
|
|
}
|
|
|
|
- (nullable UIImage *)noteToSelfImageWithConversationColorName:(ConversationColorName)conversationColorName
|
|
{
|
|
UIImage *baseImage = [[UIImage imageNamed:@"note-to-self-avatar"] asTintedImageWithColor:UIColor.whiteColor];
|
|
UIColor *backgroundColor = [OWSConversationColor conversationColorOrDefaultForColorName:conversationColorName].themeColor;
|
|
|
|
CGFloat paddingFactor = 1.6;
|
|
CGFloat paddedWidth = baseImage.size.width * paddingFactor;
|
|
CGFloat paddedheight = baseImage.size.height * paddingFactor;
|
|
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(paddedWidth, paddedheight), NO, 0.0);
|
|
CGContextRef _Nullable context = UIGraphicsGetCurrentContext();
|
|
if (context == nil) {
|
|
OWSFailDebug(@"failure: context was unexpectedly nil");
|
|
return nil;
|
|
}
|
|
[backgroundColor setFill];
|
|
CGContextFillRect(context, CGRectMake(0, 0, paddedWidth, paddedheight));
|
|
|
|
CGPoint origin = CGPointMake((paddedWidth - baseImage.size.width) / 2.0f,
|
|
(paddedheight - baseImage.size.height) / 2.0f);
|
|
[baseImage drawAtPoint:origin];
|
|
|
|
UIImage *paddedImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
if (paddedImage == nil) {
|
|
OWSFailDebug(@"failure: paddedImage was unexpectedly nil");
|
|
return nil;
|
|
}
|
|
UIGraphicsEndImageContext();
|
|
|
|
return paddedImage;
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|