diff --git a/protobuf/OWSSignalServiceProtos.proto b/protobuf/OWSSignalServiceProtos.proto index af05291a8..94b20de0a 100644 --- a/protobuf/OWSSignalServiceProtos.proto +++ b/protobuf/OWSSignalServiceProtos.proto @@ -97,6 +97,7 @@ message SyncMessage { message Contacts { optional AttachmentPointer blob = 1; + optional bool isComplete = 2 [default = false]; } message Groups { diff --git a/src/Contacts/SignalAccount.h b/src/Contacts/SignalAccount.h new file mode 100644 index 000000000..ea52d17b3 --- /dev/null +++ b/src/Contacts/SignalAccount.h @@ -0,0 +1,51 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +@class Contact; +@class SignalRecipient; +@class YapDatabaseReadTransaction; + +// This class represents a single valid Signal account. +// +// * Contacts with multiple signal accounts will correspond to +// multiple instances of SignalAccount. +// * For non-contacts, the contact property will be nil. +// +// New instances of SignalAccount for active accounts are +// created every time we do a contacts intersection (e.g. +// in response to a change to the device contacts). +@interface SignalAccount : NSObject + +// An E164 value identifying the signal account. +// +// This is the key property of this class and it +// will always be non-null. +@property (nonatomic, readonly) NSString *recipientId; + +// This property is optional and will not be set for +// non-contact account. +@property (nonatomic, nullable) Contact *contact; + +@property (nonatomic) BOOL hasMultipleAccountContact; + +// For contacts with more than one signal account, +// this is a label for the account. +@property (nonatomic) NSString *multipleAccountLabelText; + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithSignalRecipient:(SignalRecipient *)signalRecipient; + +- (instancetype)initWithRecipientId:(NSString *)recipientId; + +// In most cases this should be non-null. This should only +// be null in the case where the SignalRecipient was +// deleted before this property was accessed. +- (nullable SignalRecipient *)signalRecipientWithTransaction:(YapDatabaseReadTransaction *)transaction; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Contacts/SignalAccount.m b/src/Contacts/SignalAccount.m new file mode 100644 index 000000000..8f68f9505 --- /dev/null +++ b/src/Contacts/SignalAccount.m @@ -0,0 +1,51 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "SignalAccount.h" +#import "SignalRecipient.h" +#import "TSStorageManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SignalAccount () + +@property (nonatomic) NSString *recipientId; + +@end + +#pragma mark - + +@implementation SignalAccount + +- (instancetype)initWithSignalRecipient:(SignalRecipient *)signalRecipient +{ + if (self = [super init]) { + OWSAssert(signalRecipient); + + _recipientId = signalRecipient.uniqueId; + } + return self; +} + +- (instancetype)initWithRecipientId:(NSString *)recipientId +{ + if (self = [super init]) { + OWSAssert(recipientId.length > 0); + + _recipientId = recipientId; + } + return self; +} + +- (nullable SignalRecipient *)signalRecipientWithTransaction:(YapDatabaseReadTransaction *)transaction +{ + OWSAssert([NSThread isMainThread]); + OWSAssert(transaction); + + return [SignalRecipient recipientWithTextSecureIdentifier:self.recipientId withTransaction:transaction]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/Devices/OWSContactsOutputStream.h b/src/Devices/OWSContactsOutputStream.h index 6cdcebaeb..0db555900 100644 --- a/src/Devices/OWSContactsOutputStream.h +++ b/src/Devices/OWSContactsOutputStream.h @@ -1,14 +1,16 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSChunkedOutputStream.h" NS_ASSUME_NONNULL_BEGIN -@class Contact; +@class SignalAccount; @interface OWSContactsOutputStream : OWSChunkedOutputStream -- (void)writeContact:(Contact *)contact; +- (void)writeSignalAccount:(SignalAccount *)signalAccount; @end diff --git a/src/Devices/OWSContactsOutputStream.m b/src/Devices/OWSContactsOutputStream.m index 740148261..81487940e 100644 --- a/src/Devices/OWSContactsOutputStream.m +++ b/src/Devices/OWSContactsOutputStream.m @@ -1,28 +1,34 @@ -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// #import "OWSContactsOutputStream.h" #import "Contact.h" #import "MIMETypeUtil.h" #import "OWSSignalServiceProtos.pb.h" +#import "SignalAccount.h" #import NS_ASSUME_NONNULL_BEGIN @implementation OWSContactsOutputStream -- (void)writeContact:(Contact *)contact +- (void)writeSignalAccount:(SignalAccount *)signalAccount { + OWSAssert(signalAccount); + OWSAssert(signalAccount.contact); + OWSSignalServiceProtosContactDetailsBuilder *contactBuilder = [OWSSignalServiceProtosContactDetailsBuilder new]; - [contactBuilder setName:contact.fullName]; - [contactBuilder setNumber:contact.textSecureIdentifiers.firstObject]; + [contactBuilder setName:signalAccount.contact.fullName]; + [contactBuilder setNumber:signalAccount.recipientId]; NSData *avatarPng; - if (contact.image) { + if (signalAccount.contact.image) { OWSSignalServiceProtosContactDetailsAvatarBuilder *avatarBuilder = [OWSSignalServiceProtosContactDetailsAvatarBuilder new]; [avatarBuilder setContentType:OWSMimeTypeImagePng]; - avatarPng = UIImagePNGRepresentation(contact.image); + avatarPng = UIImagePNGRepresentation(signalAccount.contact.image); [avatarBuilder setLength:(uint32_t)avatarPng.length]; [contactBuilder setAvatarBuilder:avatarBuilder]; } @@ -33,7 +39,7 @@ NS_ASSUME_NONNULL_BEGIN [self.delegateStream writeRawVarint32:contactDataLength]; [self.delegateStream writeRawData:contactData]; - if (contact.image) { + if (signalAccount.contact.image) { [self.delegateStream writeRawData:avatarPng]; } } diff --git a/src/Messages/DeviceSyncing/OWSSyncContactsMessage.m b/src/Messages/DeviceSyncing/OWSSyncContactsMessage.m index 0f3266c92..9e9fd365b 100644 --- a/src/Messages/DeviceSyncing/OWSSyncContactsMessage.m +++ b/src/Messages/DeviceSyncing/OWSSyncContactsMessage.m @@ -8,6 +8,7 @@ #import "NSDate+millisecondTimeStamp.h" #import "OWSContactsOutputStream.h" #import "OWSSignalServiceProtos.pb.h" +#import "SignalAccount.h" #import "TSAttachment.h" #import "TSAttachmentStream.h" @@ -47,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN [OWSSignalServiceProtosSyncMessageContactsBuilder new]; [contactsBuilder setBlob:attachmentProto]; + [contactsBuilder setIsComplete:YES]; OWSSignalServiceProtosSyncMessageBuilder *syncMessageBuilder = [OWSSignalServiceProtosSyncMessageBuilder new]; [syncMessageBuilder setContactsBuilder:contactsBuilder]; @@ -63,8 +65,8 @@ NS_ASSUME_NONNULL_BEGIN [dataOutputStream open]; OWSContactsOutputStream *contactsOutputStream = [OWSContactsOutputStream streamWithOutputStream:dataOutputStream]; - for (Contact *contact in self.contactsManager.signalContacts) { - [contactsOutputStream writeContact:contact]; + for (SignalAccount *signalAccount in self.contactsManager.signalAccounts) { + [contactsOutputStream writeSignalAccount:signalAccount]; } [contactsOutputStream flush]; diff --git a/src/Messages/OWSSignalServiceProtos.pb.h b/src/Messages/OWSSignalServiceProtos.pb.h index c4656d7f2..9ef8c1214 100644 --- a/src/Messages/OWSSignalServiceProtos.pb.h +++ b/src/Messages/OWSSignalServiceProtos.pb.h @@ -1,6 +1,4 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// +// Generated by the protocol buffer compiler. DO NOT EDIT! #import @@ -953,13 +951,18 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro @end #define Contacts_blob @"blob" +#define Contacts_isComplete @"isComplete" @interface OWSSignalServiceProtosSyncMessageContacts : PBGeneratedMessage { @private + BOOL hasIsComplete_:1; BOOL hasBlob_:1; + BOOL isComplete_:1; OWSSignalServiceProtosAttachmentPointer* blob; } - (BOOL) hasBlob; +- (BOOL) hasIsComplete; @property (readonly, strong) OWSSignalServiceProtosAttachmentPointer* blob; +- (BOOL) isComplete; + (instancetype) defaultInstance; - (instancetype) defaultInstance; @@ -1002,6 +1005,11 @@ NSString *NSStringFromOWSSignalServiceProtosGroupContextType(OWSSignalServicePro - (OWSSignalServiceProtosSyncMessageContactsBuilder*) setBlobBuilder:(OWSSignalServiceProtosAttachmentPointerBuilder*) builderForValue; - (OWSSignalServiceProtosSyncMessageContactsBuilder*) mergeBlob:(OWSSignalServiceProtosAttachmentPointer*) value; - (OWSSignalServiceProtosSyncMessageContactsBuilder*) clearBlob; + +- (BOOL) hasIsComplete; +- (BOOL) isComplete; +- (OWSSignalServiceProtosSyncMessageContactsBuilder*) setIsComplete:(BOOL) value; +- (OWSSignalServiceProtosSyncMessageContactsBuilder*) clearIsComplete; @end #define Groups_blob @"blob" diff --git a/src/Messages/OWSSignalServiceProtos.pb.m b/src/Messages/OWSSignalServiceProtos.pb.m index 05dfe314c..5c0598aa2 100644 --- a/src/Messages/OWSSignalServiceProtos.pb.m +++ b/src/Messages/OWSSignalServiceProtos.pb.m @@ -1,6 +1,4 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// +// Generated by the protocol buffer compiler. DO NOT EDIT! #import "OWSSignalServiceProtos.pb.h" // @@protoc_insertion_point(imports) @@ -3839,6 +3837,7 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM @interface OWSSignalServiceProtosSyncMessageContacts () @property (strong) OWSSignalServiceProtosAttachmentPointer* blob; +@property BOOL isComplete; @end @implementation OWSSignalServiceProtosSyncMessageContacts @@ -3850,9 +3849,22 @@ static OWSSignalServiceProtosSyncMessageSent* defaultOWSSignalServiceProtosSyncM hasBlob_ = !!_value_; } @synthesize blob; +- (BOOL) hasIsComplete { + return !!hasIsComplete_; +} +- (void) setHasIsComplete:(BOOL) _value_ { + hasIsComplete_ = !!_value_; +} +- (BOOL) isComplete { + return !!isComplete_; +} +- (void) setIsComplete:(BOOL) _value_ { + isComplete_ = !!_value_; +} - (instancetype) init { if ((self = [super init])) { self.blob = [OWSSignalServiceProtosAttachmentPointer defaultInstance]; + self.isComplete = NO; } return self; } @@ -3875,6 +3887,9 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS if (self.hasBlob) { [output writeMessage:1 value:self.blob]; } + if (self.hasIsComplete) { + [output writeBool:2 value:self.isComplete]; + } [self.unknownFields writeToCodedOutputStream:output]; } - (SInt32) serializedSize { @@ -3887,6 +3902,9 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS if (self.hasBlob) { size_ += computeMessageSize(1, self.blob); } + if (self.hasIsComplete) { + size_ += computeBoolSize(2, self.isComplete); + } size_ += self.unknownFields.serializedSize; memoizedSerializedSize = size_; return size_; @@ -3928,6 +3946,9 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS withIndent:[NSString stringWithFormat:@"%@ ", indent]]; [output appendFormat:@"%@}\n", indent]; } + if (self.hasIsComplete) { + [output appendFormat:@"%@%@: %@\n", indent, @"isComplete", [NSNumber numberWithBool:self.isComplete]]; + } [self.unknownFields writeDescriptionTo:output withIndent:indent]; } - (void) storeInDictionary:(NSMutableDictionary *)dictionary { @@ -3936,6 +3957,9 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS [self.blob storeInDictionary:messageDictionary]; [dictionary setObject:[NSDictionary dictionaryWithDictionary:messageDictionary] forKey:@"blob"]; } + if (self.hasIsComplete) { + [dictionary setObject: [NSNumber numberWithBool:self.isComplete] forKey: @"isComplete"]; + } [self.unknownFields storeInDictionary:dictionary]; } - (BOOL) isEqual:(id)other { @@ -3949,6 +3973,8 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS return self.hasBlob == otherMessage.hasBlob && (!self.hasBlob || [self.blob isEqual:otherMessage.blob]) && + self.hasIsComplete == otherMessage.hasIsComplete && + (!self.hasIsComplete || self.isComplete == otherMessage.isComplete) && (self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields])); } - (NSUInteger) hash { @@ -3956,6 +3982,9 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS if (self.hasBlob) { hashCode = hashCode * 31 + [self.blob hash]; } + if (self.hasIsComplete) { + hashCode = hashCode * 31 + [[NSNumber numberWithBool:self.isComplete] hash]; + } hashCode = hashCode * 31 + [self.unknownFields hash]; return hashCode; } @@ -4002,6 +4031,9 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS if (other.hasBlob) { [self mergeBlob:other.blob]; } + if (other.hasIsComplete) { + [self setIsComplete:other.isComplete]; + } [self mergeUnknownFields:other.unknownFields]; return self; } @@ -4032,6 +4064,10 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS [self setBlob:[subBuilder buildPartial]]; break; } + case 16: { + [self setIsComplete:[input readBool]]; + break; + } } } } @@ -4065,6 +4101,22 @@ static OWSSignalServiceProtosSyncMessageContacts* defaultOWSSignalServiceProtosS resultContacts.blob = [OWSSignalServiceProtosAttachmentPointer defaultInstance]; return self; } +- (BOOL) hasIsComplete { + return resultContacts.hasIsComplete; +} +- (BOOL) isComplete { + return resultContacts.isComplete; +} +- (OWSSignalServiceProtosSyncMessageContactsBuilder*) setIsComplete:(BOOL) value { + resultContacts.hasIsComplete = YES; + resultContacts.isComplete = value; + return self; +} +- (OWSSignalServiceProtosSyncMessageContactsBuilder*) clearIsComplete { + resultContacts.hasIsComplete = NO; + resultContacts.isComplete = NO; + return self; +} @end @interface OWSSignalServiceProtosSyncMessageGroups () diff --git a/src/Protocols/ContactsManagerProtocol.h b/src/Protocols/ContactsManagerProtocol.h index 64317fa58..26af046da 100644 --- a/src/Protocols/ContactsManagerProtocol.h +++ b/src/Protocols/ContactsManagerProtocol.h @@ -1,14 +1,16 @@ -// Created by Frederic Jacobs on 05/12/15. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// -@class PhoneNumber; @class Contact; +@class PhoneNumber; +@class SignalAccount; @class UIImage; @protocol ContactsManagerProtocol - (NSString * _Nonnull)displayNameForPhoneIdentifier:(NSString * _Nullable)phoneNumber; -- (NSArray * _Nonnull)signalContacts; +- (NSArray * _Nonnull)signalAccounts; #if TARGET_OS_IPHONE - (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)phoneNumber;