Signal-iOS/SignalMessaging/contacts/ViewControllerUtils.m
Michael Kirk 4f80100234 Tapping on status bar returns to call
We want to render the return-to-call banner behind the status bar, so
the user can see the system clock, etc.

But normally, doing so would mean we wouldn't receive touches in the top
20px of the screen.

// FREEBIE
2018-05-24 12:28:20 -04:00

145 lines
7.0 KiB
Objective-C

//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "ViewControllerUtils.h"
#import "NSString+OWS.h"
#import "PhoneNumber.h"
#import <AVFoundation/AVFoundation.h>
#import <SignalMessaging/Environment.h>
#import <SignalServiceKit/PhoneNumberUtil.h>
NS_ASSUME_NONNULL_BEGIN
NSString *const TappedStatusBarNotification = @"TappedStatusBarNotification";
const NSUInteger kMin2FAPinLength = 4;
const NSUInteger kMax2FAPinLength = 16;
@implementation ViewControllerUtils
+ (void)phoneNumberTextField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)insertionText
countryCode:(NSString *)countryCode
{
// Phone numbers takes many forms.
//
// * We only want to let the user enter decimal digits.
// * The user shouldn't have to enter hyphen, parentheses or whitespace;
// the phone number should be formatted automatically.
// * The user should be able to copy and paste freely.
// * Invalid input should be simply ignored.
//
// We accomplish this by being permissive and trying to "take as much of the user
// input as possible".
//
// * Always accept deletes.
// * Ignore invalid input.
// * Take partial input if possible.
NSString *oldText = textField.text;
// Construct the new contents of the text field by:
// 1. Determining the "left" substring: the contents of the old text _before_ the deletion range.
// Filtering will remove non-decimal digit characters like hyphen "-".
NSString *left = [oldText substringToIndex:range.location].digitsOnly;
// 2. Determining the "right" substring: the contents of the old text _after_ the deletion range.
NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly;
// 3. Determining the "center" substring: the contents of the new insertion text.
NSString *center = insertionText.digitsOnly;
// 4. Construct the "raw" new text by concatenating left, center and right.
NSString *textAfterChange = [[left stringByAppendingString:center] stringByAppendingString:right];
// 4a. Ensure we don't exceed the maximum length for a e164 phone number,
// 15 digits, per: https://en.wikipedia.org/wiki/E.164
//
// NOTE: The actual limit is 18, not 15, because of certain invalid phone numbers in Germany.
// https://github.com/googlei18n/libphonenumber/blob/master/FALSEHOODS.md
const int kMaxPhoneNumberLength = 18;
if (textAfterChange.length > kMaxPhoneNumberLength) {
textAfterChange = [textAfterChange substringToIndex:kMaxPhoneNumberLength];
}
// 5. Construct the "formatted" new text by inserting a hyphen if necessary.
// reformat the phone number, trying to keep the cursor beside the inserted or deleted digit
bool isJustDeletion = insertionText.length == 0;
NSUInteger cursorPositionAfterChange = MIN(left.length + center.length, textAfterChange.length);
NSString *textAfterReformat =
[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:textAfterChange
withSpecifiedCountryCodeString:countryCode];
NSUInteger cursorPositionAfterReformat = [PhoneNumberUtil translateCursorPosition:cursorPositionAfterChange
from:textAfterChange
to:textAfterReformat
stickingRightward:isJustDeletion];
textField.text = textAfterReformat;
UITextPosition *pos =
[textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterReformat];
[textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]];
}
+ (void)ows2FAPINTextField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)insertionText
{
// * We only want to let the user enter decimal digits.
// * The user should be able to copy and paste freely.
// * Invalid input should be simply ignored.
//
// We accomplish this by being permissive and trying to "take as much of the user
// input as possible".
//
// * Always accept deletes.
// * Ignore invalid input.
// * Take partial input if possible.
NSString *oldText = textField.text;
// Construct the new contents of the text field by:
// 1. Determining the "left" substring: the contents of the old text _before_ the deletion range.
// Filtering will remove non-decimal digit characters.
NSString *left = [oldText substringToIndex:range.location].digitsOnly;
// 2. Determining the "right" substring: the contents of the old text _after_ the deletion range.
NSString *right = [oldText substringFromIndex:range.location + range.length].digitsOnly;
// 3. Determining the "center" substring: the contents of the new insertion text.
NSString *center = insertionText.digitsOnly;
// 4. Construct the "raw" new text by concatenating left, center and right.
NSString *textAfterChange = [[left stringByAppendingString:center] stringByAppendingString:right];
// 5. Ensure we don't exceed the maximum length for a PIN.
if (textAfterChange.length > kMax2FAPinLength) {
textAfterChange = [textAfterChange substringToIndex:kMax2FAPinLength];
}
// 6. Construct the final text.
textField.text = textAfterChange;
NSUInteger cursorPositionAfterChange = MIN(left.length + center.length, textAfterChange.length);
UITextPosition *pos =
[textField positionFromPosition:textField.beginningOfDocument offset:(NSInteger)cursorPositionAfterChange];
[textField setSelectedTextRange:[textField textRangeFromPosition:pos toPosition:pos]];
}
+ (NSString *)examplePhoneNumberForCountryCode:(NSString *)countryCode callingCode:(NSString *)callingCode
{
OWSAssert(countryCode.length > 0);
OWSAssert(callingCode.length > 0);
NSString *examplePhoneNumber = [PhoneNumberUtil examplePhoneNumberForCountryCode:countryCode];
OWSAssert(!examplePhoneNumber || [examplePhoneNumber hasPrefix:callingCode]);
if (examplePhoneNumber && [examplePhoneNumber hasPrefix:callingCode]) {
NSString *formattedPhoneNumber =
[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:examplePhoneNumber
withSpecifiedCountryCodeString:countryCode];
if (formattedPhoneNumber.length > 0) {
examplePhoneNumber = formattedPhoneNumber;
}
return [NSString
stringWithFormat:
NSLocalizedString(@"PHONE_NUMBER_EXAMPLE_FORMAT",
@"A format for a label showing an example phone number. Embeds {{the example phone number}}."),
[examplePhoneNumber substringFromIndex:callingCode.length]];
} else {
return @"";
}
}
@end
NS_ASSUME_NONNULL_END