From bfd29cd994f100f002730df9b4d7e40579fd703d Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 18 Apr 2017 17:06:21 -0400 Subject: [PATCH] Send contacts sync messages whenever the contacts change. // FREEBIE --- Signal.xcodeproj/project.pbxproj | 6 ++ Signal/src/AppDelegate.m | 5 + Signal/src/contact/OWSContactsManager.h | 2 + Signal/src/contact/OWSContactsManager.m | 5 + Signal/src/util/OWSContactsSyncing.h | 15 +++ Signal/src/util/OWSContactsSyncing.m | 132 ++++++++++++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 Signal/src/util/OWSContactsSyncing.h create mode 100644 Signal/src/util/OWSContactsSyncing.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 957512d4e..8e8bfa7e6 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -59,6 +59,7 @@ 34B3F89C1E8DF3270035BE1A /* BlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F89B1E8DF3270035BE1A /* BlockListViewController.m */; }; 34B3F89F1E8DF5490035BE1A /* OWSTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F89E1E8DF5490035BE1A /* OWSTableViewController.m */; }; 34B3F8A21E8EA6040035BE1A /* ViewControllerUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8A11E8EA6040035BE1A /* ViewControllerUtils.m */; }; + 34D5CC961EA6AFAD005515DB /* OWSContactsSyncing.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */; }; 34DFCB851E8E04B500053165 /* AddToBlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */; }; 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */; }; 450573FE1E78A06D00615BB4 /* OWS103EnableVideoCalling.m in Sources */ = {isa = PBXBuildFile; fileRef = 450573FD1E78A06D00615BB4 /* OWS103EnableVideoCalling.m */; }; @@ -428,6 +429,8 @@ 34B3F89E1E8DF5490035BE1A /* OWSTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSTableViewController.m; sourceTree = ""; }; 34B3F8A01E8EA6040035BE1A /* ViewControllerUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewControllerUtils.h; sourceTree = ""; }; 34B3F8A11E8EA6040035BE1A /* ViewControllerUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewControllerUtils.m; sourceTree = ""; }; + 34D5CC941EA6AFAD005515DB /* OWSContactsSyncing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSyncing.h; sourceTree = ""; }; + 34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSyncing.m; sourceTree = ""; }; 34DFCB831E8E04B400053165 /* AddToBlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToBlockListViewController.h; sourceTree = ""; }; 34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToBlockListViewController.m; sourceTree = ""; }; 34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = ""; }; @@ -1191,6 +1194,8 @@ BFB074C619A5611000F2947C /* ObservableValue.m */, 76EB04EE18170B33006006FC /* Operation.h */, 76EB04EF18170B33006006FC /* Operation.m */, + 34D5CC941EA6AFAD005515DB /* OWSContactsSyncing.h */, + 34D5CC951EA6AFAD005515DB /* OWSContactsSyncing.m */, 45CD81F01DC03A22004C9430 /* OWSLogger.h */, 45CD81F11DC03A22004C9430 /* OWSLogger.m */, 45666F541D9B2827008FE134 /* OWSScrubbingLogFormatter.h */, @@ -2057,6 +2062,7 @@ 34B3F8731E8DF1700035BE1A /* AttachmentApprovalViewController.swift in Sources */, B6B1013C196D213F007E3930 /* SignalKeyingStorage.m in Sources */, B62F5E101C2980B4000D370C /* NSData+ows_StripToken.m in Sources */, + 34D5CC961EA6AFAD005515DB /* OWSContactsSyncing.m in Sources */, 45E2E9201E153B3D00457AA0 /* Strings.swift in Sources */, 34B3F88B1E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.m in Sources */, 45666F7E1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 5aefeefbe..dec7d3c3d 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -9,6 +9,7 @@ #import "Environment.h" #import "NotificationsManager.h" #import "OWSContactsManager.h" +#import "OWSContactsSyncing.h" #import "OWSStaleNotificationObserver.h" #import "Pastelog.h" #import "PropertyListPreferences.h" @@ -45,6 +46,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; @property (nonatomic, retain) UIWindow *screenProtectionWindow; @property (nonatomic) OWSIncomingMessageReadObserver *incomingMessageReadObserver; @property (nonatomic) OWSStaleNotificationObserver *staleNotificationObserver; +@property (nonatomic) OWSContactsSyncing *contactsSyncing; @end @@ -186,6 +188,9 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; RTCInitializeSSL(); }]; + self.contactsSyncing = [[OWSContactsSyncing alloc] initWithContactsManager:[Environment getCurrent].contactsManager + messageSender:[Environment getCurrent].messageSender]; + return YES; } diff --git a/Signal/src/contact/OWSContactsManager.h b/Signal/src/contact/OWSContactsManager.h index 36cc0a5d8..eb25b1360 100644 --- a/Signal/src/contact/OWSContactsManager.h +++ b/Signal/src/contact/OWSContactsManager.h @@ -43,6 +43,8 @@ extern NSString *const OWSContactsManagerSignalRecipientsDidChangeNotification; - (nullable UIImage *)imageForPhoneIdentifier:(nullable NSString *)identifier; - (NSAttributedString *)formattedFullNameForContact:(Contact *)contact font:(UIFont *)font; +- (BOOL)hasAddressBook; + + (NSComparator _Nonnull)contactComparator; @end diff --git a/Signal/src/contact/OWSContactsManager.m b/Signal/src/contact/OWSContactsManager.m index ed8840f07..474083eb9 100644 --- a/Signal/src/contact/OWSContactsManager.m +++ b/Signal/src/contact/OWSContactsManager.m @@ -587,6 +587,11 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in return contact.image; } +- (BOOL)hasAddressBook +{ + return self.addressBookReference; +} + #pragma mark - Logging + (NSString *)tag diff --git a/Signal/src/util/OWSContactsSyncing.h b/Signal/src/util/OWSContactsSyncing.h new file mode 100644 index 000000000..8a79f6e4b --- /dev/null +++ b/Signal/src/util/OWSContactsSyncing.h @@ -0,0 +1,15 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "SignalsViewController.h" + +@class OWSContactsManager; +@class OWSMessageSender; + +@interface OWSContactsSyncing : NSObject + +- (instancetype)initWithContactsManager:(OWSContactsManager *)contactsManager + messageSender:(OWSMessageSender *)messageSender; + +@end diff --git a/Signal/src/util/OWSContactsSyncing.m b/Signal/src/util/OWSContactsSyncing.m new file mode 100644 index 000000000..69aaab5e0 --- /dev/null +++ b/Signal/src/util/OWSContactsSyncing.m @@ -0,0 +1,132 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSContactsSyncing.h" +#import "OWSContactsManager.h" +#import "TSAccountManager.h" +#import +#import +#import +#import +#import + +NSString *const kTSStorageManagerOWSContactsSyncingCollection = @"kTSStorageManagerOWSContactsSyncingCollection"; +NSString *const kTSStorageManagerOWSContactsSyncingLastMessageKey = + @"kTSStorageManagerOWSContactsSyncingLastMessageKey"; + +@interface OWSContactsSyncing () + +@property (nonatomic, readonly) OWSContactsManager *contactsManager; +@property (nonatomic, readonly) OWSMessageSender *messageSender; +@property (nonatomic) BOOL isRequestInFlight; + +@end + +@implementation OWSContactsSyncing + +- (instancetype)initWithContactsManager:(OWSContactsManager *)contactsManager + messageSender:(OWSMessageSender *)messageSender +{ + self = [super init]; + + if (!self) { + return self; + } + + OWSAssert(contactsManager); + OWSAssert(messageSender); + + _contactsManager = contactsManager; + _messageSender = messageSender; + + OWSSingletonAssert(); + + __weak OWSContactsSyncing *weakSelf = self; + [contactsManager.getObservableContacts watchLatestValue:^(id latestValue) { + [weakSelf sendSyncContactsMessageIfPossible]; + } + onThread:[NSThread mainThread] + untilCancelled:nil]; + + return self; +} + +#pragma mark - Methods + +- (void)sendSyncContactsMessageIfNecessary +{ + if (self.isRequestInFlight) { + // De-bounce. It's okay if we ignore some new changes; + // `sendSyncContactsMessageIfPossible` is called fairly + // often so we'll sync soon. + return; + } + + OWSSyncContactsMessage *syncContactsMessage = + [[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager]; + + NSData *messageData = [syncContactsMessage buildPlainTextAttachmentData]; + + NSData *lastMessageData = + [[TSStorageManager sharedManager] objectForKey:kTSStorageManagerOWSContactsSyncingLastMessageKey + inCollection:kTSStorageManagerOWSContactsSyncingCollection]; + + if (lastMessageData && [lastMessageData isEqual:messageData]) { + // Ignore redundant contacts sync message. + return; + } + + self.isRequestInFlight = YES; + + [self.messageSender sendTemporaryAttachmentData:[syncContactsMessage buildPlainTextAttachmentData] + contentType:OWSMimeTypeApplicationOctetStream + inMessage:syncContactsMessage + success:^{ + DDLogInfo(@"%@ Successfully sent contacts sync message.", self.tag); + + [[TSStorageManager sharedManager] setObject:messageData + forKey:kTSStorageManagerOWSContactsSyncingLastMessageKey + inCollection:kTSStorageManagerOWSContactsSyncingCollection]; + + dispatch_async(dispatch_get_main_queue(), ^{ + self.isRequestInFlight = NO; + }); + } + failure:^(NSError *error) { + DDLogError(@"%@ Failed to send contacts sync message with error: %@", self.tag, error); + + dispatch_async(dispatch_get_main_queue(), ^{ + self.isRequestInFlight = NO; + }); + }]; +} + +- (void)sendSyncContactsMessageIfPossible +{ + if (![self.contactsManager hasAddressBook]) { + // Don't bother until the contacts manager has finished setup. + return; + } + + [[TSAccountManager sharedInstance] ifRegistered:YES + runAsync:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self sendSyncContactsMessageIfNecessary]; + }); + }]; +} + +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + +@end