[SSK] Migrating changes from obsolete SSK repo.

// FREEBIE
pull/1/head
Matthew Chen 7 years ago
parent 03aacbd682
commit c8b2e22a3b

@ -3,7 +3,6 @@
//
#import <Foundation/Foundation.h>
#import <libPhoneNumber-iOS/NBPhoneNumberUtil.h>
#define COUNTRY_CODE_PREFIX @"+"
@ -13,15 +12,8 @@
* Everything that expects a valid phone number should take a PhoneNumber, not a string, to avoid stringly typing.
*
*/
@interface PhoneNumber : NSObject {
@private
NBPhoneNumber *phoneNumber;
@private
NSString *e164;
}
+ (PhoneNumber *)phoneNumberFromText:(NSString *)text andRegion:(NSString *)regionCode;
+ (PhoneNumber *)phoneNumberFromUserSpecifiedText:(NSString *)text;
@interface PhoneNumber : NSObject
+ (PhoneNumber *)phoneNumberFromE164:(NSString *)text;
+ (PhoneNumber *)tryParsePhoneNumberFromText:(NSString *)text fromRegion:(NSString *)regionCode;

@ -2,16 +2,40 @@
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "NBAsYouTypeFormatter.h"
#import "NBPhoneNumber.h"
#import "PhoneNumber.h"
#import "PhoneNumberUtil.h"
#import <libPhoneNumber-iOS/NBAsYouTypeFormatter.h>
#import <libPhoneNumber-iOS/NBMetadataHelper.h>
#import <libPhoneNumber-iOS/NBPhoneMetaData.h>
#import <libPhoneNumber-iOS/NBPhoneNumber.h>
#import <libPhoneNumber-iOS/NBPhoneNumberUtil.h>
static NSString *const RPDefaultsKeyPhoneNumberString = @"RPDefaultsKeyPhoneNumberString";
static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneNumberCanonical";
@interface PhoneNumber ()
@property (nonatomic, readonly) NBPhoneNumber *phoneNumber;
@property (nonatomic, readonly) NSString *e164;
@end
#pragma mark -
@implementation PhoneNumber
- (instancetype)initWithPhoneNumber:(NBPhoneNumber *)phoneNumber e164:(NSString *)e164
{
if (self = [self init]) {
OWSAssert(phoneNumber);
OWSAssert(e164.length > 0);
_phoneNumber = phoneNumber;
_e164 = e164;
}
return self;
}
+ (PhoneNumber *)phoneNumberFromText:(NSString *)text andRegion:(NSString *)regionCode {
OWSAssert(text != nil);
OWSAssert(regionCode != nil);
@ -32,10 +56,7 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
return nil;
}
PhoneNumber *phoneNumber = [PhoneNumber new];
phoneNumber->phoneNumber = number;
phoneNumber->e164 = e164;
return phoneNumber;
return [[PhoneNumber alloc] initWithPhoneNumber:number e164:e164];
}
+ (PhoneNumber *)phoneNumberFromUserSpecifiedText:(NSString *)text {
@ -118,8 +139,92 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
return [self phoneNumberFromUserSpecifiedText:sanitizedString];
}
+ (NSString *)nationalPrefixTransformRuleForDefaultRegion
{
static NSString *result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *defaultRegionCode = [self defaultRegionCode];
NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
NBPhoneMetaData *defaultRegionMetadata = [helper getMetadataForRegion:defaultRegionCode];
result = defaultRegionMetadata.nationalPrefixTransformRule;
});
return result;
}
// clientPhoneNumber is the local user's phone number and should never change.
+ (NSString *)nationalPrefixTransformRuleForClientPhoneNumber:(NSString *)clientPhoneNumber
{
if (clientPhoneNumber.length < 1) {
return nil;
}
static NSString *result = nil;
static NSString *cachedClientPhoneNumber = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// clientPhoneNumber is the local user's phone number and should never change.
NSNumber *localCallingCode = [[PhoneNumber phoneNumberFromE164:clientPhoneNumber] getCountryCode];
if (localCallingCode != nil) {
NSString *localCallingCodePrefix = [NSString stringWithFormat:@"+%@", localCallingCode];
NSString *localCountryCode =
[PhoneNumberUtil.sharedUtil probableCountryCodeForCallingCode:localCallingCodePrefix];
if (localCountryCode && ![localCountryCode isEqualToString:[self defaultRegionCode]]) {
NBMetadataHelper *helper = [[NBMetadataHelper alloc] init];
NBPhoneMetaData *localNumberRegionMetadata = [helper getMetadataForRegion:localCountryCode];
result = localNumberRegionMetadata.nationalPrefixTransformRule;
}
}
cachedClientPhoneNumber = [clientPhoneNumber copy];
});
OWSAssert([cachedClientPhoneNumber isEqualToString:clientPhoneNumber]);
return result;
}
+ (NSArray<PhoneNumber *> *)tryParsePhoneNumbersFromsUserSpecifiedText:(NSString *)text
clientPhoneNumber:(NSString *)clientPhoneNumber
{
NSMutableArray<PhoneNumber *> *result =
[[self tryParsePhoneNumbersFromNormalizedText:text clientPhoneNumber:clientPhoneNumber] mutableCopy];
// A handful of countries (Mexico, Argentina, etc.) require a "national" prefix after
// their country calling code.
//
// It's a bit hacky, but we reconstruct these national prefixes from libPhoneNumber's
// parsing logic. It's okay if we botch this a little. The risk is that we end up with
// some misformatted numbers with extra non-numeric regex syntax. These erroneously
// parsed numbers will never be presented to the user, since they'll never survive the
// contacts intersection.
//
// 1. Try to apply a "national prefix" using the phone's region.
NSString *nationalPrefixTransformRuleForDefaultRegion = [self nationalPrefixTransformRuleForDefaultRegion];
if ([nationalPrefixTransformRuleForDefaultRegion containsString:@"$1"]) {
NSString *normalizedText =
[nationalPrefixTransformRuleForDefaultRegion stringByReplacingOccurrencesOfString:@"$1" withString:text];
if (![normalizedText containsString:@"$"]) {
[result addObjectsFromArray:[self tryParsePhoneNumbersFromNormalizedText:normalizedText
clientPhoneNumber:clientPhoneNumber]];
}
}
// 2. Try to apply a "national prefix" using the region that corresponds to the
// calling code for the local phone number.
NSString *nationalPrefixTransformRuleForClientPhoneNumber =
[self nationalPrefixTransformRuleForClientPhoneNumber:clientPhoneNumber];
if ([nationalPrefixTransformRuleForClientPhoneNumber containsString:@"$1"]) {
NSString *normalizedText =
[nationalPrefixTransformRuleForClientPhoneNumber stringByReplacingOccurrencesOfString:@"$1"
withString:text];
if (![normalizedText containsString:@"$"]) {
[result addObjectsFromArray:[self tryParsePhoneNumbersFromNormalizedText:normalizedText
clientPhoneNumber:clientPhoneNumber]];
}
}
return [result copy];
}
+ (NSArray<PhoneNumber *> *)tryParsePhoneNumbersFromNormalizedText:(NSString *)text
clientPhoneNumber:(NSString *)clientPhoneNumber
{
OWSAssert(text != nil);
@ -210,30 +315,32 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
}
- (NSURL *)toSystemDialerURL {
NSString *link = [NSString stringWithFormat:@"telprompt://%@", e164];
NSString *link = [NSString stringWithFormat:@"telprompt://%@", self.e164];
return [NSURL URLWithString:link];
}
- (NSString *)toE164 {
return e164;
return self.e164;
}
- (NSNumber *)getCountryCode {
return phoneNumber.countryCode;
return self.phoneNumber.countryCode;
}
- (BOOL)isValid {
return [[PhoneNumberUtil sharedUtil].nbPhoneNumberUtil isValidNumber:phoneNumber];
return [[PhoneNumberUtil sharedUtil].nbPhoneNumberUtil isValidNumber:self.phoneNumber];
}
- (NSString *)localizedDescriptionForUser {
NBPhoneNumberUtil *phoneUtil = [PhoneNumberUtil sharedUtil].nbPhoneNumberUtil;
NSError *formatError = nil;
NSString *pretty = [phoneUtil format:phoneNumber numberFormat:NBEPhoneNumberFormatINTERNATIONAL error:&formatError];
NSString *pretty =
[phoneUtil format:self.phoneNumber numberFormat:NBEPhoneNumberFormatINTERNATIONAL error:&formatError];
if (formatError != nil)
return e164;
if (formatError != nil) {
return self.e164;
}
return pretty;
}
@ -242,18 +349,18 @@ static NSString *const RPDefaultsKeyPhoneNumberCanonical = @"RPDefaultsKeyPhoneN
}
- (NSString *)description {
return e164;
return self.e164;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:phoneNumber forKey:RPDefaultsKeyPhoneNumberString];
[encoder encodeObject:e164 forKey:RPDefaultsKeyPhoneNumberCanonical];
[encoder encodeObject:self.phoneNumber forKey:RPDefaultsKeyPhoneNumberString];
[encoder encodeObject:self.e164 forKey:RPDefaultsKeyPhoneNumberCanonical];
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super init])) {
phoneNumber = [decoder decodeObjectForKey:RPDefaultsKeyPhoneNumberString];
e164 = [decoder decodeObjectForKey:RPDefaultsKeyPhoneNumberCanonical];
_phoneNumber = [decoder decodeObjectForKey:RPDefaultsKeyPhoneNumberString];
_e164 = [decoder decodeObjectForKey:RPDefaultsKeyPhoneNumberCanonical];
}
return self;
}

@ -5,10 +5,29 @@
#import <XCTest/XCTest.h>
#import "PhoneNumber.h"
@interface NSArray (PhoneNumberTest)
@end
#pragma mark -
@implementation NSArray (PhoneNumberTest)
- (id)test_firstObject
{
return (self.count > 0) ? self[0] : nil;
}
@end
#pragma mark -
@interface PhoneNumberTest : XCTestCase
@end
#pragma mark -
@implementation PhoneNumberTest
-(void)testE164 {
@ -59,4 +78,72 @@
XCTAssertEqualObjects(@"+13235551234", [actual toE164]);
}
- (NSArray<NSString *> *)unpackTryParsePhoneNumbersFromsUserSpecifiedText:(NSString *)text
clientPhoneNumber:(NSString *)clientPhoneNumber
{
NSArray<PhoneNumber *> *phoneNumbers =
[PhoneNumber tryParsePhoneNumbersFromsUserSpecifiedText:text clientPhoneNumber:clientPhoneNumber];
NSMutableArray<NSString *> *result = [NSMutableArray new];
for (PhoneNumber *phoneNumber in phoneNumbers) {
[result addObject:phoneNumber.toE164];
}
return result;
}
- (void)testTryParsePhoneNumbersFromsUserSpecifiedText_SimpleUSA
{
NSArray<NSString *> *parsed =
[self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"323 555 1234" clientPhoneNumber:@"+13213214321"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+13235551234"]);
XCTAssertEqualObjects(parsed.test_firstObject, @"+13235551234");
parsed = [self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"323-555-1234" clientPhoneNumber:@"+13213214321"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+13235551234"]);
XCTAssertEqualObjects(parsed.test_firstObject, @"+13235551234");
parsed = [self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"323.555.1234" clientPhoneNumber:@"+13213214321"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+13235551234"]);
XCTAssertEqualObjects(parsed.test_firstObject, @"+13235551234");
parsed =
[self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"1-323-555-1234" clientPhoneNumber:@"+13213214321"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+13235551234"]);
XCTAssertEqualObjects(parsed.test_firstObject, @"+13235551234");
parsed = [self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"+13235551234" clientPhoneNumber:@"+13213214321"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+13235551234"]);
XCTAssertEqualObjects(parsed.test_firstObject, @"+13235551234");
}
- (void)testTryParsePhoneNumbersFromsUserSpecifiedText_Mexico1
{
NSArray<NSString *> *parsed =
[self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"528341639157" clientPhoneNumber:@"+528341639144"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+528341639157"]);
parsed = [self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"8341639157" clientPhoneNumber:@"+528341639144"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+528341639157"]);
parsed = [self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"341639157" clientPhoneNumber:@"+528341639144"];
XCTAssertLessThanOrEqual(1, parsed.count);
// The parsing logic should try adding Mexico's national prefix for cell numbers "1"
// after the country code.
XCTAssertTrue([parsed containsObject:@"+521341639157"]);
parsed = [self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"528341639157" clientPhoneNumber:@"+13213214321"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+528341639157"]);
parsed = [self unpackTryParsePhoneNumbersFromsUserSpecifiedText:@"13235551234" clientPhoneNumber:@"+528341639144"];
XCTAssertLessThanOrEqual(1, parsed.count);
XCTAssertTrue([parsed containsObject:@"+13235551234"]);
}
@end

Loading…
Cancel
Save