Merge branch 'charlesmchen/deregistration'

pull/1/head
Matthew Chen 7 years ago
commit 9e2716d5a8

@ -231,6 +231,7 @@
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; };
34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E3EF0C1EFC235B007F6822 /* DebugUIDiskUsage.m */; };
34E3EF101EFC2684007F6822 /* DebugUIPage.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E3EF0F1EFC2684007F6822 /* DebugUIPage.m */; };
34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E5DC8120D8050D00C08145 /* RegistrationUtils.m */; };
34E88D262098C5AE00A608F4 /* ContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E88D252098C5AE00A608F4 /* ContactViewController.swift */; };
34E8A8D12085238A00B272B1 /* ProtoParsingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 34E8A8D02085238900B272B1 /* ProtoParsingTest.m */; };
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; };
@ -894,6 +895,8 @@
34E3EF0C1EFC235B007F6822 /* DebugUIDiskUsage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIDiskUsage.m; sourceTree = "<group>"; };
34E3EF0E1EFC2684007F6822 /* DebugUIPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIPage.h; sourceTree = "<group>"; };
34E3EF0F1EFC2684007F6822 /* DebugUIPage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIPage.m; sourceTree = "<group>"; };
34E5DC8020D8050D00C08145 /* RegistrationUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegistrationUtils.h; sourceTree = "<group>"; };
34E5DC8120D8050D00C08145 /* RegistrationUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegistrationUtils.m; sourceTree = "<group>"; };
34E88D252098C5AE00A608F4 /* ContactViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = "<group>"; };
34E8A8D02085238900B272B1 /* ProtoParsingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProtoParsingTest.m; sourceTree = "<group>"; };
34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = "<group>"; };
@ -2115,6 +2118,8 @@
4579431C1E7C8CE9008ED0C0 /* Pastelog.h */,
4579431D1E7C8CE9008ED0C0 /* Pastelog.m */,
450DF2041E0D74AC003D14BE /* Platform.swift */,
34E5DC8020D8050D00C08145 /* RegistrationUtils.h */,
34E5DC8120D8050D00C08145 /* RegistrationUtils.m */,
4521C3BF1F59F3BA00B4C582 /* TextFieldHelper.swift */,
FCFA64B11A24F29E0007FB87 /* UI Categories */,
);
@ -3235,6 +3240,7 @@
452037D11EE84975004E4CDF /* DebugUISessionState.m in Sources */,
D221A09A169C9E5E00537ABF /* main.m in Sources */,
34B3F87B1E8DF1700035BE1A /* ExperienceUpgradesPageViewController.swift in Sources */,
34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */,
452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */,
45638BDC1F3DD0D400128435 /* DebugUICalling.swift in Sources */,
45464DBC1DFA041F001D3FD6 /* DataChannelMessage.swift in Sources */,

@ -14,6 +14,7 @@
#import "PrivacySettingsTableViewController.h"
#import "ProfileViewController.h"
#import "PushManager.h"
#import "RegistrationUtils.h"
#import "Signal-Swift.h"
#import <SignalMessaging/Environment.h>
#import <SignalMessaging/OWSContactsManager.h>
@ -144,19 +145,25 @@
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UILabel *accessoryLabel = [UILabel new];
accessoryLabel.font = [UIFont ows_regularFontWithSize:18.f];
switch ([TSSocketManager sharedManager].state) {
case SocketManagerStateClosed:
accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_OFFLINE", @"");
accessoryLabel.textColor = [UIColor ows_redColor];
break;
case SocketManagerStateConnecting:
accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_CONNECTING", @"");
accessoryLabel.textColor = [UIColor ows_yellowColor];
break;
case SocketManagerStateOpen:
accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_CONNECTED", @"");
accessoryLabel.textColor = [UIColor ows_greenColor];
break;
if (TSAccountManager.sharedInstance.isDeregistered) {
accessoryLabel.text = NSLocalizedString(
@"NETWORK_STATUS_DEREGISTERED", @"Error indicating that this device is no longer registered.");
accessoryLabel.textColor = [UIColor ows_redColor];
} else {
switch ([TSSocketManager sharedManager].state) {
case SocketManagerStateClosed:
accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_OFFLINE", @"");
accessoryLabel.textColor = [UIColor ows_redColor];
break;
case SocketManagerStateConnecting:
accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_CONNECTING", @"");
accessoryLabel.textColor = [UIColor ows_yellowColor];
break;
case SocketManagerStateOpen:
accessoryLabel.text = NSLocalizedString(@"NETWORK_STATUS_CONNECTED", @"");
accessoryLabel.textColor = [UIColor ows_greenColor];
break;
}
}
[accessoryLabel sizeToFit];
cell.accessoryView = accessoryLabel;
@ -227,12 +234,23 @@
cell.selectionStyle = UITableViewCellSelectionStyleNone;
const CGFloat kButtonHeight = 40.f;
OWSFlatButton *button = [OWSFlatButton buttonWithTitle:NSLocalizedString(@"SETTINGS_DELETE_ACCOUNT_BUTTON", @"")
font:[OWSFlatButton fontForHeight:kButtonHeight]
titleColor:[UIColor whiteColor]
backgroundColor:[UIColor ows_destructiveRedColor]
target:self
selector:@selector(unregisterUser)];
OWSFlatButton *button;
if (TSAccountManager.sharedInstance.isDeregistered) {
button = [OWSFlatButton
buttonWithTitle:NSLocalizedString(@"SETTINGS_REREGISTER_BUTTON", @"Label for re-registration button.")
font:[OWSFlatButton fontForHeight:kButtonHeight]
titleColor:[UIColor whiteColor]
backgroundColor:[UIColor ows_destructiveRedColor]
target:self
selector:@selector(reregisterUser)];
} else {
button = [OWSFlatButton buttonWithTitle:NSLocalizedString(@"SETTINGS_DELETE_ACCOUNT_BUTTON", @"")
font:[OWSFlatButton fontForHeight:kButtonHeight]
titleColor:[UIColor whiteColor]
backgroundColor:[UIColor ows_destructiveRedColor]
target:self
selector:@selector(unregisterUser)];
}
[cell.contentView addSubview:button];
[button autoSetDimension:ALDimensionHeight toSize:kButtonHeight];
[button autoVCenterInSuperview];
@ -390,7 +408,7 @@
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - Table view data source
#pragma mark - Unregister & Re-register
- (void)unregisterUser
{
@ -427,6 +445,11 @@
}];
}
- (void)reregisterUser
{
[RegistrationUtils showReregistrationUIFromViewController:self];
}
#pragma mark - Socket Status Notifications
- (void)observeNotifications

@ -24,14 +24,6 @@
NS_ASSUME_NONNULL_BEGIN
@interface TSAccountManager (DebugUI)
- (void)resetForRegistration;
@end
#pragma mark -
@interface OWSStorage (DebugUI)
- (NSData *)databasePassword;
@ -147,7 +139,12 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)reregister
{
DDLogInfo(@"%@ re-registering.", self.logTag);
[[TSAccountManager sharedInstance] resetForRegistration];
if (![[TSAccountManager sharedInstance] resetForReregistration]) {
OWSFail(@"%@ could not reset for re-registration.", self.logTag);
return;
}
[[Environment current].preferences unsetRecordedAPNSTokens];
RegistrationViewController *viewController = [RegistrationViewController new];

@ -11,6 +11,7 @@
#import "OWSPrimaryStorage.h"
#import "ProfileViewController.h"
#import "PushManager.h"
#import "RegistrationUtils.h"
#import "Signal-Swift.h"
#import "SignalApp.h"
#import "TSAccountManager.h"
@ -25,6 +26,7 @@
#import <SignalServiceKit/OWSBlockingManager.h>
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/OWSMessageUtils.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSOutgoingMessage.h>
#import <SignalServiceKit/Threading.h>
#import <YapDatabase/YapDatabase.h>
@ -73,6 +75,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
// Views
@property (nonatomic) NSLayoutConstraint *hideDeregisteredViewConstraint;
@property (nonatomic) NSLayoutConstraint *hideArchiveReminderViewConstraint;
@property (nonatomic) NSLayoutConstraint *hideMissingContactsPermissionViewConstraint;
@ -159,6 +162,10 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
selector:@selector(yapDatabaseModifiedExternally:)
name:YapDatabaseModifiedExternallyNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deregistrationStateDidChange:)
name:DeregistrationStateDidChangeNotification
object:nil];
}
- (void)dealloc
@ -184,6 +191,13 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
[self reloadTableViewData];
}
- (void)deregistrationStateDidChange:(id)notification
{
OWSAssertIsOnMainThread();
[self updateReminderViews];
}
#pragma mark - View Life Cycle
- (void)loadView
@ -197,12 +211,32 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
[SignalApp.sharedApp setHomeViewController:self];
}
UIStackView *reminderStackView = [UIStackView new];
reminderStackView.axis = UILayoutConstraintAxisVertical;
reminderStackView.spacing = 0;
[self.view addSubview:reminderStackView];
[reminderStackView autoPinWidthToSuperview];
[reminderStackView autoPinToTopLayoutGuideOfViewController:self withInset:0];
__weak HomeViewController *weakSelf = self;
ReminderView *deregisteredView =
[ReminderView nagWithText:NSLocalizedString(@"DEREGISTRATION_WARNING",
@"Label warning the user that they have been de-registered.")
tapAction:^{
HomeViewController *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[RegistrationUtils showReregistrationUIFromViewController:strongSelf];
}];
[reminderStackView addArrangedSubview:deregisteredView];
self.hideDeregisteredViewConstraint = [deregisteredView autoSetDimension:ALDimensionHeight toSize:0];
self.hideDeregisteredViewConstraint.priority = UILayoutPriorityRequired;
ReminderView *archiveReminderView =
[ReminderView explanationWithText:NSLocalizedString(@"INBOX_VIEW_ARCHIVE_MODE_REMINDER",
@"Label reminding the user that they are in archive mode.")];
[self.view addSubview:archiveReminderView];
[archiveReminderView autoPinWidthToSuperview];
[archiveReminderView autoPinToTopLayoutGuideOfViewController:self withInset:0];
[reminderStackView addArrangedSubview:archiveReminderView];
self.hideArchiveReminderViewConstraint = [archiveReminderView autoSetDimension:ALDimensionHeight toSize:0];
self.hideArchiveReminderViewConstraint.priority = UILayoutPriorityRequired;
@ -212,9 +246,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
tapAction:^{
[[UIApplication sharedApplication] openSystemSettings];
}];
[self.view addSubview:missingContactsPermissionView];
[missingContactsPermissionView autoPinWidthToSuperview];
[missingContactsPermissionView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:archiveReminderView];
[reminderStackView addArrangedSubview:missingContactsPermissionView];
self.hideMissingContactsPermissionViewConstraint =
[missingContactsPermissionView autoSetDimension:ALDimensionHeight toSize:0];
self.hideMissingContactsPermissionViewConstraint.priority = UILayoutPriorityRequired;
@ -228,7 +260,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
[self.view addSubview:self.tableView];
[self.tableView autoPinWidthToSuperview];
[self.tableView autoPinEdgeToSuperviewEdge:ALEdgeBottom];
[self.tableView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:missingContactsPermissionView];
[self.tableView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:reminderStackView];
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 60;
@ -258,12 +290,18 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
{
BOOL shouldHideArchiveReminderView = self.homeViewMode != HomeViewMode_Archive;
BOOL shouldHideMissingContactsPermissionView = !self.shouldShowMissingContactsPermissionView;
BOOL shouldHideDeregisteredView = !TSAccountManager.sharedInstance.isDeregistered;
if (self.hideArchiveReminderViewConstraint.active == shouldHideArchiveReminderView
&& self.hideMissingContactsPermissionViewConstraint.active == shouldHideMissingContactsPermissionView) {
&& self.hideMissingContactsPermissionViewConstraint.active == shouldHideMissingContactsPermissionView
&& self.hideDeregisteredViewConstraint.active == shouldHideDeregisteredView) {
return;
}
self.hideArchiveReminderViewConstraint.active = shouldHideArchiveReminderView;
self.hideMissingContactsPermissionViewConstraint.active = shouldHideMissingContactsPermissionView;
self.hideDeregisteredViewConstraint.active = shouldHideDeregisteredView;
[self.view setNeedsLayout];
[self.view layoutSubviews];
}
@ -788,11 +826,8 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
// Constrain to cell margins.
[stackView autoPinEdgeToSuperviewMargin:ALEdgeLeading relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewMargin:ALEdgeTrailing relation:NSLayoutRelationGreaterThanOrEqual];
// Ensure that the cell's contents never overflow the cell bounds.
// We pin to the superview _edge_ and not _margin_ for the purposes
// of overflow, so that changes to the margins do not trip these safe guards.
[stackView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:0 relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewMargin:ALEdgeTop];
[stackView autoPinEdgeToSuperviewMargin:ALEdgeBottom];
return cell;
}

@ -112,16 +112,25 @@ NS_ASSUME_NONNULL_BEGIN
[titleLabel autoSetDimension:ALDimensionHeight toSize:40];
[titleLabel autoHCenterInSuperview];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton
setTitle:NSLocalizedString(@"VERIFICATION_BACK_BUTTON", @"button text for back button on verification view")
forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
backButton.titleLabel.font = [UIFont ows_mediumFontWithSize:14.f];
[header addSubview:backButton];
[backButton autoPinLeadingToSuperviewMarginWithInset:10.f];
[backButton autoAlignAxis:ALAxisHorizontal toSameAxisOfView:titleLabel];
[backButton addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
// This view is used in more than one context.
//
// * Usually, it is pushed atop RegistrationViewController in which
// case we want a "back" button.
// * It can also be used to re-register from the app's "de-registration"
// views, in which case RegistrationViewController is not used and we
// do _not_ want a "back" button.
if (self.navigationController.viewControllers.count > 1) {
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton
setTitle:NSLocalizedString(@"VERIFICATION_BACK_BUTTON", @"button text for back button on verification view")
forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
backButton.titleLabel.font = [UIFont ows_mediumFontWithSize:14.f];
[header addSubview:backButton];
[backButton autoPinLeadingToSuperviewMarginWithInset:10.f];
[backButton autoAlignAxis:ALAxisHorizontal toSameAxisOfView:titleLabel];
[backButton addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
_phoneNumberLabel = [UILabel new];
_phoneNumberLabel.textColor = [UIColor ows_darkGrayColor];

@ -278,6 +278,61 @@ NSString *const kKeychainKey_LastRegisteredPhoneNumber = @"kKeychainKey_LastRegi
[self.activateButton setEnabled:YES];
[self.spinnerView stopAnimating];
[self.phoneNumberTextField becomeFirstResponder];
if ([TSAccountManager sharedInstance].isReregistering) {
// If re-registering, pre-populate the country (country code, calling code, country name)
// and phone number state.
NSString *_Nullable phoneNumberE164 = [TSAccountManager sharedInstance].reregisterationPhoneNumber;
if (!phoneNumberE164) {
OWSFail(@"%@ Could not resume re-registration; missing phone number.", self.logTag);
} else if ([self tryToApplyPhoneNumberE164:phoneNumberE164]) {
// Don't let user edit their phone number while re-registering.
self.phoneNumberTextField.enabled = NO;
}
}
}
- (BOOL)tryToApplyPhoneNumberE164:(NSString *)phoneNumberE164
{
OWSAssert(phoneNumberE164);
if (phoneNumberE164.length < 1) {
OWSFail(@"%@ Could not resume re-registration; invalid phoneNumberE164.", self.logTag);
return NO;
}
PhoneNumber *_Nullable parsedPhoneNumber = [PhoneNumber phoneNumberFromE164:phoneNumberE164];
if (!parsedPhoneNumber) {
OWSFail(@"%@ Could not resume re-registration; couldn't parse phoneNumberE164.", self.logTag);
return NO;
}
NSNumber *_Nullable callingCode = parsedPhoneNumber.getCountryCode;
if (!callingCode) {
OWSFail(@"%@ Could not resume re-registration; missing callingCode.", self.logTag);
return NO;
}
NSString *callingCodeText = [NSString stringWithFormat:@"+%d", callingCode.intValue];
NSArray<NSString *> *_Nullable countryCodes =
[PhoneNumberUtil.sharedThreadLocal countryCodesFromCallingCode:callingCodeText];
if (countryCodes.count < 1) {
OWSFail(@"%@ Could not resume re-registration; unknown countryCode.", self.logTag);
return NO;
}
NSString *countryCode = countryCodes.firstObject;
NSString *_Nullable countryName = [PhoneNumberUtil countryNameFromCountryCode:countryCode];
if (!countryName) {
OWSFail(@"%@ Could not resume re-registration; unknown countryName.", self.logTag);
return NO;
}
if (![phoneNumberE164 hasPrefix:callingCodeText]) {
OWSFail(@"%@ Could not resume re-registration; non-matching calling code.", self.logTag);
return NO;
}
NSString *phoneNumberWithoutCallingCode = [phoneNumberE164 substringFromIndex:callingCodeText.length];
[self updateCountryWithName:countryName callingCode:callingCodeText countryCode:countryCode];
self.phoneNumberTextField.text = phoneNumberWithoutCallingCode;
return YES;
}
#pragma mark - Country
@ -385,6 +440,11 @@ NSString *const kKeychainKey_LastRegisteredPhoneNumber = @"kKeychainKey_LastRegi
- (void)countryCodeRowWasTapped:(UIGestureRecognizer *)sender
{
if (TSAccountManager.sharedInstance.isReregistering) {
// Don't let user edit their phone number while re-registering.
return;
}
if (sender.state == UIGestureRecognizerStateRecognized) {
[self changeCountryCodeTapped];
}

@ -0,0 +1,15 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@interface RegistrationUtils : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (void)showReregistrationUIFromViewController:(UIViewController *)fromViewController;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,91 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "RegistrationUtils.h"
#import "CodeVerificationViewController.h"
#import "OWSNavigationController.h"
#import <SignalMessaging/Environment.h>
#import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalServiceKit/TSAccountManager.h>
NS_ASSUME_NONNULL_BEGIN
@implementation RegistrationUtils
+ (void)showReregistrationUIFromViewController:(UIViewController *)fromViewController
{
UIAlertController *actionSheetController =
[UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[actionSheetController
addAction:[UIAlertAction
actionWithTitle:NSLocalizedString(@"DEREGISTRATION_REREGISTER_WITH_SAME_PHONE_NUMBER",
@"Label for button that lets users re-register using the same phone number.")
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction *action) {
[RegistrationUtils reregisterWithFromViewController:fromViewController];
}]];
[actionSheetController addAction:[OWSAlerts cancelAction]];
[fromViewController presentViewController:actionSheetController animated:YES completion:nil];
}
+ (void)reregisterWithFromViewController:(UIViewController *)fromViewController
{
DDLogInfo(@"%@ reregisterWithSamePhoneNumber.", self.logTag);
if (![[TSAccountManager sharedInstance] resetForReregistration]) {
OWSFail(@"%@ could not reset for re-registration.", self.logTag);
return;
}
[[Environment current].preferences unsetRecordedAPNSTokens];
[ModalActivityIndicatorViewController
presentFromViewController:fromViewController
canCancel:NO
backgroundBlock:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
[TSAccountManager
registerWithPhoneNumber:[TSAccountManager sharedInstance].reregisterationPhoneNumber
success:^{
DDLogInfo(@"%@ re-registering: send verification code succeeded.", self.logTag);
dispatch_async(dispatch_get_main_queue(), ^{
[modalActivityIndicator dismissWithCompletion:^{
CodeVerificationViewController *viewController =
[CodeVerificationViewController new];
OWSNavigationController *navigationController =
[[OWSNavigationController alloc] initWithRootViewController:viewController];
navigationController.navigationBarHidden = YES;
[UIApplication sharedApplication].delegate.window.rootViewController
= navigationController;
}];
});
}
failure:^(NSError *error) {
DDLogError(@"%@ re-registering: send verification code failed.", self.logTag);
dispatch_async(dispatch_get_main_queue(), ^{
[modalActivityIndicator dismissWithCompletion:^{
if (error.code == 400) {
[OWSAlerts showAlertWithTitle:NSLocalizedString(@"REGISTRATION_ERROR", nil)
message:NSLocalizedString(
@"REGISTRATION_NON_VALID_NUMBER", nil)];
} else {
[OWSAlerts showAlertWithTitle:error.localizedDescription
message:error.localizedRecoverySuggestion];
}
}];
});
}
smsVerification:YES];
}];
}
@end
NS_ASSUME_NONNULL_END

@ -89,12 +89,7 @@ class ReminderView: UIView {
// Margin: top and bottom 12 left and right 16.
// Label
switch (mode) {
case .nag:
label.font = UIFont.ows_regularFont(withSize: 14)
case .explanation:
label.font = UIFont.ows_dynamicTypeSubheadline
}
label.font = UIFont.ows_dynamicTypeSubheadline
container.addSubview(label)
label.textColor = UIColor.black.withAlphaComponent(0.9)
label.numberOfLines = 0

@ -647,6 +647,12 @@
/* Title of the alert before redirecting to GitHub Issues. */
"DEBUG_LOG_GITHUB_ISSUE_ALERT_TITLE" = "GitHub Redirection";
/* Label for button that lets users re-register using the same phone number. */
"DEREGISTRATION_REREGISTER_WITH_SAME_PHONE_NUMBER" = "Re-register this phone number";
/* Label warning the user that they have been de-registered. */
"DEREGISTRATION_WARNING" = "Device no longer registered! This is likely because you registered your phone number with Signal on a different device. Tap to re-register.";
/* {{Short Date}} when device last communicated with Signal Server. */
"DEVICE_LAST_ACTIVE_AT_LABEL" = "Last active: %@";
@ -659,7 +665,7 @@
/* table cell label in conversation settings */
"DISAPPEARING_MESSAGES" = "Disappearing Messages";
/* Info Message when added to {{group name}} which has enabled message expiration after {{time amount}}, see the *_TIME_AMOUNT strings for context. */
/* Info Message when added to a group which has enabled disappearing messages. Embeds {{time amount}} before messages disappear, see the *_TIME_AMOUNT strings for context. */
"DISAPPEARING_MESSAGES_CONFIGURATION_GROUP_EXISTING_FORMAT" = "Messages in this conversation will disappear after %@.";
/* subheading in conversation settings */
@ -1309,6 +1315,9 @@
/* No comment provided by engineer. */
"NETWORK_STATUS_CONNECTING" = "Connecting";
/* Error indicating that this device is no longer registered. */
"NETWORK_STATUS_DEREGISTERED" = "No Longer Registered";
/* No comment provided by engineer. */
"NETWORK_STATUS_HEADER" = "Network Status";
@ -1972,6 +1981,9 @@
/* An explanation of the 'read receipts' setting. */
"SETTINGS_READ_RECEIPTS_SECTION_FOOTER" = "See and share when messages have been read. This setting is optional and applies to all conversations.";
/* Label for re-registration button. */
"SETTINGS_REREGISTER_BUTTON" = "Re-register";
/* Label for the 'screen lock activity timeout' setting of the privacy settings. */
"SETTINGS_SCREEN_LOCK_ACTIVITY_TIMEOUT" = "Screen Lock Timeout";

@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
extern NSString *const TSRegistrationErrorDomain;
extern NSString *const TSRegistrationErrorUserInfoHTTPStatus;
extern NSString *const RegistrationStateDidChangeNotification;
extern NSString *const DeregistrationStateDidChangeNotification;
extern NSString *const kNSNotificationName_LocalNumberDidChange;
@class OWSPrimaryStorage;
@ -116,6 +117,26 @@ extern NSString *const kNSNotificationName_LocalNumberDidChange;
+ (void)unregisterTextSecureWithSuccess:(void (^)(void))success failure:(void (^)(NSError *error))failureBlock;
#pragma mark - De-Registration
// De-registration reflects whether or not the "last known contact"
// with the service was:
//
// * A 403 from the service, indicating de-registration.
// * A successful auth'd request _or_ websocket connection indicating
// valid registration.
- (BOOL)isDeregistered;
- (void)setIsDeregistered:(BOOL)isDeregistered;
#pragma mark - Re-registration
// Re-registration is the process of re-registering _with the same phone number_.
// Returns YES on success.
- (BOOL)resetForReregistration;
- (NSString *)reregisterationPhoneNumber;
- (BOOL)isReregistering;
@end
NS_ASSUME_NONNULL_END

@ -16,6 +16,7 @@
#import "TSPreKeyManager.h"
#import "TSVerifyCodeRequest.h"
#import "YapDatabaseConnection+OWS.h"
#import "YapDatabaseTransaction+OWS.h"
#import <YapDatabase/YapDatabase.h>
NS_ASSUME_NONNULL_BEGIN
@ -23,9 +24,12 @@ NS_ASSUME_NONNULL_BEGIN
NSString *const TSRegistrationErrorDomain = @"TSRegistrationErrorDomain";
NSString *const TSRegistrationErrorUserInfoHTTPStatus = @"TSHTTPStatus";
NSString *const RegistrationStateDidChangeNotification = @"RegistrationStateDidChangeNotification";
NSString *const DeregistrationStateDidChangeNotification = @"DeregistrationStateDidChangeNotification";
NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName_LocalNumberDidChange";
NSString *const TSAccountManager_RegisteredNumberKey = @"TSStorageRegisteredNumberKey";
NSString *const TSAccountManager_IsDeregisteredKey = @"TSAccountManager_IsDeregisteredKey";
NSString *const TSAccountManager_ReregisteringPhoneNumberKey = @"TSAccountManager_ReregisteringPhoneNumberKey";
NSString *const TSAccountManager_LocalRegistrationIdKey = @"TSStorageLocalRegistrationId";
NSString *const TSAccountManager_UserAccountCollection = @"TSStorageUserAccountCollection";
@ -44,6 +48,8 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
@property (nonatomic, nullable) NSString *cachedLocalNumber;
@property (nonatomic, readonly) YapDatabaseConnection *dbConnection;
@property (nonatomic, nullable) NSNumber *cachedIsDeregistered;
@end
#pragma mark -
@ -101,21 +107,6 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
userInfo:nil];
}
- (void)resetForRegistration
{
@synchronized(self)
{
_isRegistered = NO;
_cachedLocalNumber = nil;
_phoneNumberAwaitingVerification = nil;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[transaction removeAllObjectsInCollection:TSAccountManager_UserAccountCollection];
[[OWSPrimaryStorage sharedManager] resetSessionStore:transaction];
}];
}
}
+ (BOOL)isRegistered
{
return [[self sharedInstance] isRegistered];
@ -152,6 +143,7 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
// Warm these cached values.
[self isRegistered];
[self localNumber];
[self isDeregistered];
}
+ (nullable NSString *)localNumber
@ -191,6 +183,13 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
[self.dbConnection setObject:localNumber
forKey:TSAccountManager_RegisteredNumberKey
inCollection:TSAccountManager_UserAccountCollection];
[self.dbConnection removeObjectForKey:TSAccountManager_ReregisteringPhoneNumberKey
inCollection:TSAccountManager_UserAccountCollection];
self.phoneNumberAwaitingVerification = nil;
self.cachedLocalNumber = localNumber;
}
}
@ -524,6 +523,91 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
}
}
#pragma mark - De-Registration
- (BOOL)isDeregistered
{
// Cache this since we access this a lot, and once set it will not change.
@synchronized(self) {
if (self.cachedIsDeregistered == nil) {
self.cachedIsDeregistered = @([self.dbConnection boolForKey:TSAccountManager_IsDeregisteredKey
inCollection:TSAccountManager_UserAccountCollection
defaultValue:NO]);
}
OWSAssert(self.cachedIsDeregistered);
return self.cachedIsDeregistered.boolValue;
}
}
- (void)setIsDeregistered:(BOOL)isDeregistered
{
@synchronized(self) {
if (self.cachedIsDeregistered && self.cachedIsDeregistered.boolValue == isDeregistered) {
return;
}
DDLogWarn(@"%@ isDeregistered: %d", self.logTag, isDeregistered);
self.cachedIsDeregistered = @(isDeregistered);
}
[self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction setObject:@(isDeregistered)
forKey:TSAccountManager_IsDeregisteredKey
inCollection:TSAccountManager_UserAccountCollection];
}];
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:DeregistrationStateDidChangeNotification
object:nil
userInfo:nil];
}
#pragma mark - Re-registration
- (BOOL)resetForReregistration
{
@synchronized(self) {
NSString *_Nullable localNumber = self.localNumber;
if (!localNumber) {
OWSFail(@"%@ can't re-register without valid local number.", self.logTag);
return NO;
}
_isRegistered = NO;
_cachedLocalNumber = nil;
_phoneNumberAwaitingVerification = nil;
_cachedIsDeregistered = nil;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction removeAllObjectsInCollection:TSAccountManager_UserAccountCollection];
[[OWSPrimaryStorage sharedManager] resetSessionStore:transaction];
[transaction setObject:localNumber
forKey:TSAccountManager_ReregisteringPhoneNumberKey
inCollection:TSAccountManager_UserAccountCollection];
}];
return YES;
}
}
- (NSString *)reregisterationPhoneNumber
{
OWSAssert([self isReregistering]);
NSString *_Nullable result = [self.dbConnection stringForKey:TSAccountManager_ReregisteringPhoneNumberKey
inCollection:TSAccountManager_UserAccountCollection];
OWSAssert(result);
return result;
}
- (BOOL)isReregistering
{
return nil !=
[self.dbConnection stringForKey:TSAccountManager_ReregisteringPhoneNumberKey
inCollection:TSAccountManager_UserAccountCollection];
}
@end
NS_ASSUME_NONNULL_END

@ -87,6 +87,11 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
// TODO: Remove this logging when the call connection issues have been resolved.
TSNetworkManagerSuccess success = ^(NSURLSessionDataTask *task, _Nullable id responseObject) {
DDLogInfo(@"%@ request succeeded : %@", self.logTag, request);
if (request.shouldHaveAuthorizationHeaders) {
[TSAccountManager.sharedInstance setIsDeregistered:NO];
}
successBlock(task, responseObject);
};
TSNetworkManagerFailure failure = [TSNetworkManager errorPrettifyingForFailureBlock:failureBlock request:request];
@ -166,6 +171,9 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
}
case 400: {
DDLogError(@"The request contains an invalid parameter : %@, %@", networkError.debugDescription, request);
[TSAccountManager.sharedInstance setIsDeregistered:YES];
failureBlock(task, error);
break;
}

@ -612,8 +612,16 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
BOOL hasSuccessStatus = 200 <= responseStatus && responseStatus <= 299;
BOOL didSucceed = hasSuccessStatus && hasValidResponse;
if (didSucceed) {
[TSAccountManager.sharedInstance setIsDeregistered:NO];
[socketMessage didSucceedWithResponseObject:responseObject];
} else {
if (responseStatus == 403) {
// This should be redundant with our check for the socket
// failing due to 403, but let's be thorough.
[TSAccountManager.sharedInstance setIsDeregistered:YES];
}
NSError *error = OWSErrorWithCodeDescription(OWSErrorCodeMessageResponseFailed,
NSLocalizedString(
@"ERROR_DESCRIPTION_RESPONSE_FAILED", @"Error indicating that a socket response failed."));
@ -666,6 +674,9 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
}
self.state = SocketManagerStateOpen;
// If socket opens, we know we're not de-registered.
[TSAccountManager.sharedInstance setIsDeregistered:NO];
}
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
@ -678,6 +689,13 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
DDLogError(@"Websocket did fail with error: %@", error);
if ([error.domain isEqualToString:SRWebSocketErrorDomain] && error.code == 2132) {
NSNumber *_Nullable statusCode = error.userInfo[SRHTTPResponseErrorKey];
if (statusCode.unsignedIntegerValue == 403) {
[TSAccountManager.sharedInstance setIsDeregistered:YES];
}
}
[self handleSocketFailure];
}
@ -690,6 +708,9 @@ NSString *const kNSNotification_SocketManagerStateDidChange = @"kNSNotification_
return;
}
// If we receive a response, we know we're not de-registered.
[TSAccountManager.sharedInstance setIsDeregistered:NO];
WebSocketResourcesWebSocketMessage *wsMessage;
@try {
wsMessage = [WebSocketResourcesWebSocketMessage parseFromData:data];

Loading…
Cancel
Save