extract and test contact searcher

// FREEBIE
This commit is contained in:
Michael Kirk 2016-06-28 08:57:35 -07:00
parent 8c6bf3cba6
commit a181d218cf
8 changed files with 147 additions and 27 deletions

View File

@ -43,7 +43,7 @@ PODS:
- ProtocolBuffers (1.9.10)
- Reachability (3.2)
- SCWaveformView (1.0.0)
- SignalServiceKit (0.0.4):
- SignalServiceKit (0.0.5):
- '25519'
- AFNetworking
- AxolotlKit
@ -137,7 +137,7 @@ CHECKOUT OPTIONS:
:commit: 225b1baa11125ea84d4b960d700834b5b0a40ee1
:git: https://github.com/WhisperSystems/JSQMessagesViewController
SignalServiceKit:
:commit: f5aac9610c274dad33c071fcdd9d2ab6b3d85f0d
:commit: 80671b247f616c3bd6264eccce5f806a4a538e68
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:commit: 587ad297eb63eb0d64d4caeb32a7da646ad1132b
@ -160,7 +160,7 @@ SPEC CHECKSUMS:
ProtocolBuffers: d088180c10072b3d24a9939a6314b7b9bcc2340b
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SCWaveformView: 52a96750255d817e300565a80c81fb643e233e07
SignalServiceKit: e27a3025c2d5c61696386e44a50ac8d2fe83151e
SignalServiceKit: f52bc6e17f717540d93b4247a93246648bf4085e
SocketRocket: 3f77ec2104cc113add553f817ad90a77114f5d43
SQLCipher: 4c768761421736a247ed6cf412d9045615d53dff
SSKeychain: c71293fa57216a40ab06c23f4085387583293de4

View File

@ -9,6 +9,9 @@
/* Begin PBXBuildFile section */
0DD55B166906AF3368995978 /* libPods-Signal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 80CD5E19DD23200E7926EEA7 /* libPods-Signal.a */; };
30209C98DABCE82064B4EAF5 /* libPods-SignalTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A33D3C7EB4B17BDBD47F0FCC /* libPods-SignalTests.a */; };
45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; };
45843D201D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; };
45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */; };
45CB2FA81CB7146C00E1B343 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */; };
4CE0E3771B954546007210CF /* TSAnimatedAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E3761B954546007210CF /* TSAnimatedAdapter.m */; };
701231B518ECAA4500D456C4 /* EvpMessageDigest.m in Sources */ = {isa = PBXBuildFile; fileRef = 701231B418ECAA4500D456C4 /* EvpMessageDigest.m */; };
@ -489,6 +492,9 @@
/* Begin PBXFileReference section */
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
454B35071D08EED80026D658 /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = translations/mk.lproj/Localizable.strings; sourceTree = "<group>"; };
45843D1D1D2236B30013E85A /* OWSContactsSearcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSearcher.h; sourceTree = "<group>"; };
45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcher.m; sourceTree = "<group>"; };
45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcherTest.m; sourceTree = "<group>"; };
45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = "Launch Screen.storyboard"; path = "Signal/src/util/Launch Screen.storyboard"; sourceTree = SOURCE_ROOT; };
45E282DE1D08E67800ADD4C8 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = translations/gl.lproj/Localizable.strings; sourceTree = "<group>"; };
45E282DF1D08E6CC00ADD4C8 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = translations/id.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -1182,6 +1188,8 @@
76EB04A818170B33006006FC /* number directory */,
76EB040818170B33006006FC /* OWSContactsManager.h */,
76EB040918170B33006006FC /* OWSContactsManager.m */,
45843D1D1D2236B30013E85A /* OWSContactsSearcher.h */,
45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */,
);
path = contact;
sourceTree = "<group>";
@ -1782,6 +1790,7 @@
isa = PBXGroup;
children = (
B660F6761C29867F00687D6E /* OWSContactsManagerTest.m */,
45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */,
);
path = contact;
sourceTree = "<group>";
@ -2594,6 +2603,7 @@
A547DD741A70A87800103EC7 /* DJWActionSheet+OWS.m in Sources */,
76EB062618170B33006006FC /* Queue.m in Sources */,
D221A09A169C9E5E00537ABF /* main.m in Sources */,
45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */,
76EB061618170B33006006FC /* AnonymousOccurrenceLogger.m in Sources */,
B6258B331C29E2E60014138E /* NotificationsManager.m in Sources */,
76EB063018170B33006006FC /* Conversions.m in Sources */,
@ -2793,6 +2803,7 @@
B660F7461C29988E00687D6E /* HelloAckPacket.m in Sources */,
B660F7471C29988E00687D6E /* HelloPacket.m in Sources */,
B660F7481C29988E00687D6E /* RecipientUnavailable.m in Sources */,
45843D201D2236B30013E85A /* OWSContactsSearcher.m in Sources */,
B660F7491C29988E00687D6E /* ShortAuthenticationStringGenerator.m in Sources */,
B660F74A1C29988E00687D6E /* ZrtpHandshakeResult.m in Sources */,
B660F74B1C29988E00687D6E /* ZrtpHandshakeSocket.m in Sources */,
@ -2806,6 +2817,7 @@
B660F7531C29988E00687D6E /* NetworkStream.m in Sources */,
B660F7541C29988E00687D6E /* SecureEndPoint.m in Sources */,
B660F7551C29988E00687D6E /* UdpSocket.m in Sources */,
45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */,
B660F7561C29988E00687D6E /* PushManager.m in Sources */,
B660F7571C29988E00687D6E /* NotificationTracker.m in Sources */,
B660F7581C29988E00687D6E /* RPAccountManager.m in Sources */,

View File

@ -23,7 +23,6 @@ typedef void (^ABReloadRequestCompletionBlock)(NSArray *contacts);
- (NSArray *)getContactsFromAddressBook:(ABAddressBookRef)addressBook;
- (Contact *)latestContactForPhoneNumber:(PhoneNumber *)phoneNumber;
- (NSArray *)latestContactsWithSearchString:(NSString *)searchString;
- (void)verifyABPermission;

View File

@ -248,12 +248,6 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
}];
}
- (NSArray *)latestContactsWithSearchString:(NSString *)searchString {
return [self.latestContactsById.allValues filter:^int(Contact *contact) {
return searchString.length == 0 || [OWSContactsManager name:contact.fullName matchesQuery:searchString];
}];
}
#pragma mark - Contact/Phone Number util
- (Contact *)contactForRecord:(ABRecordRef)record {

View File

@ -0,0 +1,16 @@
//
// OWSContactsSearcher.h
// Signal
//
// Created by Michael Kirk on 6/27/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
#import "Contact.h"
@interface OWSContactsSearcher : NSObject
- (instancetype)initWithContacts:(NSArray<Contact *> *)contacts;
- (NSArray<Contact *> *)filterWithString:(NSString *)string;
@end

View File

@ -0,0 +1,42 @@
//
// OWSContactsSearcher.m
// Signal
//
// Created by Michael Kirk on 6/27/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
#import "OWSContactsSearcher.h"
#import <SignalServiceKit/PhoneNumber.h>
@interface OWSContactsSearcher ()
@property (copy) NSArray<Contact *> *contacts;
@end
@implementation OWSContactsSearcher
- (instancetype)initWithContacts:(NSArray<Contact *> *)contacts {
self = [super init];
if (!self) return self;
_contacts = contacts;
return self;
}
- (NSArray<Contact *> *)filterWithString:(NSString *)string {
NSString *searchTerm = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([searchTerm isEqualToString:@""]) {
return self.contacts;
}
NSString *formattedNumber = [PhoneNumber removeFormattingCharacters:searchTerm];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(fullName contains[c] %@) OR (ANY parsedPhoneNumbers.toE164 contains[c] %@)", searchTerm, formattedNumber];
return [self.contacts filteredArrayUsingPredicate:predicate];
}
@end

View File

@ -12,6 +12,7 @@
#import "ContactTableViewCell.h"
#import "ContactsUpdater.h"
#import "OWSContactsSearcher.h"
#import "Environment.h"
#import "UIColor+OWS.h"
#import "UIUtil.h"
@ -26,8 +27,8 @@
@property (nonatomic, strong) UIBarButtonItem *addGroup;
@property (nonatomic, strong) UIView *loadingBackgroundView;
@property (nonatomic, strong) UIView *emptyBackgroundView;
@property (copy) NSArray<Contact *> *contacts;
@property (nonatomic) NSString *currentSearchTerm;
@property (copy) NSArray<Contact *> *contacts;
@property (copy) NSArray<Contact *> *searchResults;
@end
@ -263,16 +264,10 @@
#pragma mark - Filter
- (void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope {
// search by contact name or number
OWSContactsSearcher *contactsSearcher = [[OWSContactsSearcher alloc] initWithContacts: self.contacts];
self.searchResults = [contactsSearcher filterWithString:searchText];
NSString *formattedNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:searchText].toE164;
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:@"(fullName contains[c] %@) OR (ANY parsedPhoneNumbers.toE164 contains[c] %@)", searchText, formattedNumber];
self.searchResults = [self.contacts filteredArrayUsingPredicate:resultPredicate];
if (!self.searchResults.count && self.searchController.searchBar.text.length == 0) {
self.searchResults = self.contacts;
}
// text to a non-signal number if we have no results and a valid phone #
if (self.searchResults.count == 0 && searchText.length > 8 && formattedNumber) {
NSString *sendTextTo = NSLocalizedString(@"SEND_SMS_BUTTON", @"");
@ -426,12 +421,6 @@
}
#pragma mark - Table View delegate
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Contact *contact = [self contactForIndexPath:indexPath];
// TODO what does it mean to have non Signal contacts here?
return contact.isSignalContact ? indexPath : nil;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = [[[self contactForIndexPath:indexPath] textSecureIdentifiers] firstObject];
@ -442,7 +431,6 @@
}];
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
ContactTableViewCell *cell = (ContactTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;

View File

@ -0,0 +1,69 @@
//
// OWSContactSearcherTest.m
// Signal
//
// Created by Michael Kirk on 6/27/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "OWSContactsSearcher.h"
@interface OWSContactsSearcherTest : XCTestCase
@property Contact *meow;
@property Contact *clement;
@property OWSContactsSearcher *contactsSearcher;
@end
@implementation OWSContactsSearcherTest
- (void)setUp {
[super setUp];
self.meow = [[Contact alloc] initWithContactWithFirstName:@"Chairman"
andLastName:@"Meow"
andUserTextPhoneNumbers:@[ @"1-323-555-1234", @"+86 10 1111 2222" ]
andImage:nil
andContactID:1];
self.clement = [[Contact alloc] initWithContactWithFirstName:@"Clément"
andLastName:@"Duval"
andUserTextPhoneNumbers:@[ @"33 123456789" ]
andImage:nil
andContactID:2];
self.contactsSearcher = [[OWSContactsSearcher alloc] initWithContacts:@[self.meow, self.clement]];
}
- (void)testFilterWithStringMatchAllOnEmtpy {
XCTAssertEqualObjects((@[self.meow, self.clement]), [self.contactsSearcher filterWithString:@""]);
XCTAssertEqualObjects((@[self.meow, self.clement]), [self.contactsSearcher filterWithString:@" "]);
}
- (void)testFilterWithStringMatchByName {
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"Chairman"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"Chair"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"Meow"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"Chairman Meow"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@" Chairman Meow "]);
XCTAssertEqualObjects((@[self.meow, self.clement]), ([self.contactsSearcher filterWithString:@"C"]));
XCTAssertEqualObjects(@[], [self.contactsSearcher filterWithString:@"Chairman Meowww"]);
}
- (void)testFilterWithStringByNumber {
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"1-323-555-1234"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"+86 10 1111 2222"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"323-555-1234"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"323.555.1234"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"3235551234"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"323"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"323 555 1234"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"+1 323 555 1234"]);
XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"+13235551234"]);
XCTAssertEqualObjects((@[self.meow, self.clement]), [self.contactsSearcher filterWithString:@"1234"]);
}
@end