session-ios/Signal/src/ViewControllers/ThreadSettings/FingerprintViewController.m

548 lines
23 KiB
Mathematica
Raw Normal View History

2014-10-29 21:58:58 +01:00
//
2018-01-30 21:49:36 +01:00
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
2014-10-29 21:58:58 +01:00
//
#import "FingerprintViewController.h"
#import "FingerprintViewScanController.h"
#import "OWSBezierPathView.h"
#import "Signal-Swift.h"
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import "UIView+OWS.h"
2017-12-19 03:50:51 +01:00
#import <SignalMessaging/Environment.h>
#import <SignalMessaging/OWSContactsManager.h>
2017-12-08 17:50:35 +01:00
#import <SignalMessaging/UIUtil.h>
#import <SignalServiceKit/NSDate+OWS.h>
#import <SignalServiceKit/OWSError.h>
#import <SignalServiceKit/OWSFingerprint.h>
#import <SignalServiceKit/OWSFingerprintBuilder.h>
#import <SignalServiceKit/OWSIdentityManager.h>
#import <SignalServiceKit/OWSPrimaryStorage+SessionStore.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSInfoMessage.h>
2014-10-29 21:58:58 +01:00
NS_ASSUME_NONNULL_BEGIN
2014-12-11 00:05:41 +01:00
2017-11-08 20:04:51 +01:00
typedef void (^CustomLayoutBlock)(void);
@interface CustomLayoutView : UIView
@property (nonatomic) CustomLayoutBlock layoutBlock;
@end
#pragma mark -
@implementation CustomLayoutView
2017-06-09 13:27:36 +02:00
- (instancetype)init
{
if (self = [super init]) {
self.translatesAutoresizingMaskIntoConstraints = NO;
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.translatesAutoresizingMaskIntoConstraints = NO;
}
return self;
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
self.translatesAutoresizingMaskIntoConstraints = NO;
}
return self;
}
- (void)layoutSubviews
{
2017-06-09 13:27:36 +02:00
[super layoutSubviews];
self.layoutBlock();
}
@end
#pragma mark -
@interface FingerprintViewController () <OWSCompareSafetyNumbersActivityDelegate>
@property (nonatomic) NSString *recipientId;
@property (nonatomic) NSData *identityKey;
@property (nonatomic) TSAccountManager *accountManager;
@property (nonatomic) OWSFingerprint *fingerprint;
@property (nonatomic) NSString *contactName;
@property (nonatomic) UIBarButtonItem *shareButton;
2014-10-29 21:58:58 +01:00
@property (nonatomic) UILabel *verificationStateLabel;
@property (nonatomic) UILabel *verifyUnverifyButtonLabel;
@end
#pragma mark -
2014-10-29 21:58:58 +01:00
@implementation FingerprintViewController
+ (void)presentFromViewController:(UIViewController *)viewController recipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
OWSRecipientIdentity *_Nullable recipientIdentity =
[[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId];
if (!recipientIdentity) {
[OWSAlerts showAlertWithTitle:NSLocalizedString(@"CANT_VERIFY_IDENTITY_ALERT_TITLE",
@"Title for alert explaining that a user cannot be verified.")
message:NSLocalizedString(@"CANT_VERIFY_IDENTITY_ALERT_MESSAGE",
@"Message for alert explaining that a user cannot be verified.")];
return;
}
FingerprintViewController *fingerprintViewController = [FingerprintViewController new];
[fingerprintViewController configureWithRecipientId:recipientId];
OWSNavigationController *navigationController =
[[OWSNavigationController alloc] initWithRootViewController:fingerprintViewController];
[viewController presentViewController:navigationController animated:YES completion:nil];
}
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
_accountManager = [TSAccountManager sharedInstance];
[self observeNotifications];
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)observeNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(identityStateDidChange:)
name:kNSNotificationName_IdentityStateDidChange
object:nil];
}
- (void)configureWithRecipientId:(NSString *)recipientId
{
OWSAssert(recipientId.length > 0);
self.recipientId = recipientId;
OWSContactsManager *contactsManager = [Environment current].contactsManager;
self.contactName = [contactsManager displayNameForPhoneIdentifier:recipientId];
OWSRecipientIdentity *_Nullable recipientIdentity =
[[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId];
OWSAssert(recipientIdentity);
2017-06-09 17:56:37 +02:00
// By capturing the identity key when we enter these views, we prevent the edge case
// where the user verifies a key that we learned about while this view was open.
self.identityKey = recipientIdentity.identityKey;
OWSFingerprintBuilder *builder =
[[OWSFingerprintBuilder alloc] initWithAccountManager:self.accountManager contactsManager:contactsManager];
self.fingerprint =
[builder fingerprintWithTheirSignalId:recipientId theirIdentityKey:recipientIdentity.identityKey];
2014-12-04 00:23:36 +01:00
}
- (void)loadView
{
[super loadView];
self.title = NSLocalizedString(@"PRIVACY_VERIFICATION_TITLE", @"Navbar title");
Disappearing Messages * Per thread settings menu accessed by tapping on thread title This removed the toggle-phone behavior. You'll be able to see the phone number in the settings table view. This removed the "add contact" functionality, although it was already broken for ios>=9 (which is basically everybody). The group actions menu was absorbed into this screen * Added a confirm alert to leave group (fixes #938) * New Translation Strings * Extend "Add People" label to fit translations. * resolved issues with translations not fitting in group menu * Fix the long standing type warning where TSCalls were assigned to a TSMessageAdapter. * Can delete info messages Follow the JSQMVC pattern and put UIResponder-able content in the messageBubbleContainer. This gives us more functionality *and* allows us to delete some code. yay! It's still not yet possible to delete phone messages. =( * Fixed some compiler warnings. * xcode8 touching storyboard. So long xcode7! * Fixup multiline info messages. We were seeing info messages like "You set disappearing message timer to 10" instead of "You set disappearing message timer to 10 seconds." Admittedly this isn't a very good fix, as now one liners feel like they have too much padding. If the message is well over one line, we were wrapping properly, but there's a problem when the message is *just barely* two lines, the cell height grows, but the label still thinks it's just one line (as evinced by the one line appearing in the center of the label frame. The result being that the last word of the label is cropped. * Disable group actions after leaving group. // FREEBIE
2016-09-21 14:37:51 +02:00
self.navigationItem.leftBarButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop
target:self
action:@selector(closeButton)];
self.shareButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction
target:self
action:@selector(didTapShareButton)];
self.navigationItem.rightBarButtonItem = self.shareButton;
[self createViews];
}
- (void)createViews
{
UIColor *darkGrey = [UIColor colorWithRGBHex:0x404040];
self.view.backgroundColor = [UIColor whiteColor];
// Verify/Unverify Button
UIView *verifyUnverifyButton = [UIView new];
[verifyUnverifyButton
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(verifyUnverifyButtonTapped:)]];
[self.view addSubview:verifyUnverifyButton];
[verifyUnverifyButton autoPinWidthToSuperview];
[verifyUnverifyButton autoPinToBottomLayoutGuideOfViewController:self withInset:0];
UIView *verifyUnverifyPillbox = [UIView new];
verifyUnverifyPillbox.backgroundColor = [UIColor ows_materialBlueColor];
verifyUnverifyPillbox.layer.cornerRadius = 3.f;
verifyUnverifyPillbox.clipsToBounds = YES;
[verifyUnverifyButton addSubview:verifyUnverifyPillbox];
[verifyUnverifyPillbox autoHCenterInSuperview];
[verifyUnverifyPillbox autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:ScaleFromIPhone5To7Plus(10.f, 15.f)];
[verifyUnverifyPillbox autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:ScaleFromIPhone5To7Plus(10.f, 20.f)];
UILabel *verifyUnverifyButtonLabel = [UILabel new];
self.verifyUnverifyButtonLabel = verifyUnverifyButtonLabel;
verifyUnverifyButtonLabel.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(14.f, 20.f)];
verifyUnverifyButtonLabel.textColor = [UIColor whiteColor];
verifyUnverifyButtonLabel.textAlignment = NSTextAlignmentCenter;
[verifyUnverifyPillbox addSubview:verifyUnverifyButtonLabel];
[verifyUnverifyButtonLabel autoPinWidthToSuperviewWithMargin:ScaleFromIPhone5To7Plus(50.f, 50.f)];
[verifyUnverifyButtonLabel autoPinHeightToSuperviewWithMargin:ScaleFromIPhone5To7Plus(8.f, 8.f)];
// Learn More
UIView *learnMoreButton = [UIView new];
[learnMoreButton
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(learnMoreButtonTapped:)]];
[self.view addSubview:learnMoreButton];
[learnMoreButton autoPinWidthToSuperview];
[learnMoreButton autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:verifyUnverifyButton withOffset:0];
UILabel *learnMoreLabel = [UILabel new];
learnMoreLabel.attributedText = [[NSAttributedString alloc]
initWithString:NSLocalizedString(@"PRIVACY_SAFETY_NUMBERS_LEARN_MORE",
@"Label for a link to more information about safety numbers and verification.")
attributes:@{
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid),
}];
learnMoreLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(13.f, 16.f)];
learnMoreLabel.textColor = [UIColor ows_materialBlueColor];
learnMoreLabel.textAlignment = NSTextAlignmentCenter;
[learnMoreButton addSubview:learnMoreLabel];
[learnMoreLabel autoPinWidthToSuperviewWithMargin:16.f];
[learnMoreLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:ScaleFromIPhone5To7Plus(5.f, 10.f)];
[learnMoreLabel autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:ScaleFromIPhone5To7Plus(5.f, 10.f)];
// Instructions
NSString *instructionsFormat = NSLocalizedString(@"PRIVACY_VERIFICATION_INSTRUCTIONS",
@"Paragraph(s) shown alongside the safety number when verifying privacy with {{contact name}}");
UILabel *instructionsLabel = [UILabel new];
instructionsLabel.text = [NSString stringWithFormat:instructionsFormat, self.contactName];
instructionsLabel.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(11.f, 14.f)];
instructionsLabel.textColor = darkGrey;
instructionsLabel.textAlignment = NSTextAlignmentCenter;
instructionsLabel.numberOfLines = 0;
instructionsLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.view addSubview:instructionsLabel];
[instructionsLabel autoPinWidthToSuperviewWithMargin:16.f];
[instructionsLabel autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:learnMoreButton withOffset:0];
// Fingerprint Label
UILabel *fingerprintLabel = [UILabel new];
fingerprintLabel.text = self.fingerprint.displayableText;
fingerprintLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:ScaleFromIPhone5To7Plus(20.f, 23.f)];
fingerprintLabel.textColor = darkGrey;
fingerprintLabel.numberOfLines = 3;
fingerprintLabel.lineBreakMode = NSLineBreakByTruncatingTail;
fingerprintLabel.adjustsFontSizeToFitWidth = YES;
[fingerprintLabel
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(fingerprintLabelTapped:)]];
fingerprintLabel.userInteractionEnabled = YES;
[self.view addSubview:fingerprintLabel];
[fingerprintLabel autoPinWidthToSuperviewWithMargin:ScaleFromIPhone5To7Plus(50.f, 60.f)];
[fingerprintLabel autoPinEdge:ALEdgeBottom
toEdge:ALEdgeTop
ofView:instructionsLabel
withOffset:-ScaleFromIPhone5To7Plus(8.f, 15.f)];
// Fingerprint Image
CustomLayoutView *fingerprintView = [CustomLayoutView new];
[self.view addSubview:fingerprintView];
[fingerprintView autoPinWidthToSuperview];
[fingerprintView autoPinEdge:ALEdgeBottom
toEdge:ALEdgeTop
ofView:fingerprintLabel
withOffset:-ScaleFromIPhone5To7Plus(10.f, 15.f)];
[fingerprintView
addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(fingerprintViewTapped:)]];
fingerprintView.userInteractionEnabled = YES;
OWSBezierPathView *fingerprintCircle = [OWSBezierPathView new];
[fingerprintCircle setConfigureShapeLayerBlock:^(CAShapeLayer *layer, CGRect bounds) {
layer.fillColor = [UIColor colorWithWhite:0.9f alpha:1.f].CGColor;
CGFloat size = MIN(bounds.size.width, bounds.size.height);
CGRect circle = CGRectMake((bounds.size.width - size) * 0.5f, (bounds.size.height - size) * 0.5f, size, size);
layer.path = [UIBezierPath bezierPathWithOvalInRect:circle].CGPath;
}];
[fingerprintView addSubview:fingerprintCircle];
2017-10-02 20:26:03 +02:00
[fingerprintCircle autoPinToSuperviewEdges];
UIImageView *fingerprintImageView = [UIImageView new];
fingerprintImageView.image = self.fingerprint.image;
2017-06-09 13:27:36 +02:00
// Don't antialias QR Codes.
fingerprintImageView.layer.magnificationFilter = kCAFilterNearest;
fingerprintImageView.layer.minificationFilter = kCAFilterNearest;
[fingerprintView addSubview:fingerprintImageView];
UILabel *scanLabel = [UILabel new];
scanLabel.text = NSLocalizedString(@"PRIVACY_TAP_TO_SCAN", @"Button that shows the 'scan with camera' view.");
scanLabel.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(14.f, 16.f)];
scanLabel.textColor = [UIColor colorWithWhite:0.15f alpha:1.f];
[scanLabel sizeToFit];
[fingerprintView addSubview:scanLabel];
fingerprintView.layoutBlock = ^{
CGFloat size = round(MIN(fingerprintView.width, fingerprintView.height) * 0.675f);
fingerprintImageView.frame = CGRectMake(
round((fingerprintView.width - size) * 0.5f), round((fingerprintView.height - size) * 0.5f), size, size);
CGFloat scanY = round(fingerprintImageView.bottom
+ ((fingerprintView.height - fingerprintImageView.bottom) - scanLabel.height) * 0.33f);
scanLabel.frame = CGRectMake(
round((fingerprintView.width - scanLabel.width) * 0.5f), scanY, scanLabel.width, scanLabel.height);
};
// Verification State
UILabel *verificationStateLabel = [UILabel new];
self.verificationStateLabel = verificationStateLabel;
verificationStateLabel.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(16.f, 20.f)];
verificationStateLabel.textColor = darkGrey;
verificationStateLabel.textAlignment = NSTextAlignmentCenter;
verificationStateLabel.numberOfLines = 0;
verificationStateLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.view addSubview:verificationStateLabel];
[verificationStateLabel autoPinWidthToSuperviewWithMargin:16.f];
// Bind height of label to height of two lines of text.
// This should always be sufficient, and will prevent the view's
// layout from changing if the user is marked as verified or not
// verified.
[verificationStateLabel autoSetDimension:ALDimensionHeight
toSize:round(verificationStateLabel.font.lineHeight * 2.25f)];
[verificationStateLabel autoPinToTopLayoutGuideOfViewController:self withInset:ScaleFromIPhone5To7Plus(15.f, 20.f)];
[verificationStateLabel autoPinEdge:ALEdgeBottom
toEdge:ALEdgeTop
ofView:fingerprintView
withOffset:-ScaleFromIPhone5To7Plus(10.f, 15.f)];
[self updateVerificationStateLabel];
}
- (void)updateVerificationStateLabel
{
OWSAssert(self.recipientId.length > 0);
2018-01-30 22:41:25 +01:00
BOOL isVerified = [[OWSIdentityManager sharedManager] verificationStateForRecipientId:self.recipientId]
== OWSVerificationStateVerified;
if (isVerified) {
NSMutableAttributedString *labelText = [NSMutableAttributedString new];
if (isVerified) {
// Show a "checkmark" if this user is verified.
[labelText
appendAttributedString:[[NSAttributedString alloc]
initWithString:@"\uf00c "
attributes:@{
NSFontAttributeName : [UIFont
ows_fontAwesomeFont:self.verificationStateLabel.font.pointSize],
}]];
}
[labelText
appendAttributedString:
[[NSAttributedString alloc]
initWithString:[NSString stringWithFormat:NSLocalizedString(@"PRIVACY_IDENTITY_IS_VERIFIED_FORMAT",
@"Label indicating that the user is verified. Embeds "
@"{{the user's name or phone number}}."),
self.contactName]]];
self.verificationStateLabel.attributedText = labelText;
self.verifyUnverifyButtonLabel.text = NSLocalizedString(
@"PRIVACY_UNVERIFY_BUTTON", @"Button that lets user mark another user's identity as unverified.");
} else {
self.verificationStateLabel.text = [NSString
stringWithFormat:NSLocalizedString(@"PRIVACY_IDENTITY_IS_NOT_VERIFIED_FORMAT",
@"Label indicating that the user is not verified. Embeds {{the user's name or phone "
@"number}}."),
self.contactName];
NSMutableAttributedString *buttonText = [NSMutableAttributedString new];
// Show a "checkmark" if this user is not verified.
[buttonText
appendAttributedString:[[NSAttributedString alloc]
initWithString:@"\uf00c "
attributes:@{
NSFontAttributeName : [UIFont
ows_fontAwesomeFont:self.verifyUnverifyButtonLabel.font.pointSize],
}]];
[buttonText appendAttributedString:
[[NSAttributedString alloc]
initWithString:NSLocalizedString(@"PRIVACY_VERIFY_BUTTON",
@"Button that lets user mark another user's identity as verified.")]];
self.verifyUnverifyButtonLabel.attributedText = buttonText;
}
[self.view setNeedsLayout];
}
#pragma mark -
- (void)showSharingActivityWithCompletion:(nullable void (^)(void))completionHandler
{
2017-11-08 20:04:51 +01:00
DDLogDebug(@"%@ Sharing safety numbers", self.logTag);
OWSCompareSafetyNumbersActivity *compareActivity = [[OWSCompareSafetyNumbersActivity alloc] initWithDelegate:self];
NSString *shareFormat = NSLocalizedString(
@"SAFETY_NUMBER_SHARE_FORMAT", @"Snippet to share {{safety number}} with a friend. sent e.g. via SMS");
NSString *shareString = [NSString stringWithFormat:shareFormat, self.fingerprint.displayableText];
UIActivityViewController *activityController =
[[UIActivityViewController alloc] initWithActivityItems:@[ shareString ]
applicationActivities:@[ compareActivity ]];
activityController.completionWithItemsHandler = ^void(UIActivityType __nullable activityType,
BOOL completed,
NSArray *__nullable returnedItems,
NSError *__nullable activityError) {
if (completionHandler) {
completionHandler();
}
};
// This value was extracted by inspecting `activityType` in the activityController.completionHandler
NSString *const iCloudActivityType = @"com.apple.CloudDocsUI.AddToiCloudDrive";
activityController.excludedActivityTypes = @[
UIActivityTypePostToFacebook,
UIActivityTypePostToWeibo,
UIActivityTypeAirDrop,
UIActivityTypePostToTwitter,
iCloudActivityType // This isn't being excluded. RADAR https://openradar.appspot.com/27493621
];
[self presentViewController:activityController animated:YES completion:nil];
}
#pragma mark - OWSCompareSafetyNumbersActivityDelegate
- (void)compareSafetyNumbersActivitySucceededWithActivity:(OWSCompareSafetyNumbersActivity *)activity
{
[self showVerificationSucceeded];
}
- (void)compareSafetyNumbersActivity:(OWSCompareSafetyNumbersActivity *)activity failedWithError:(NSError *)error
{
[self showVerificationFailedWithError:error];
}
- (void)showVerificationSucceeded
{
[FingerprintViewScanController showVerificationSucceeded:self
identityKey:self.identityKey
recipientId:self.recipientId
contactName:self.contactName
2017-11-08 20:04:51 +01:00
tag:self.logTag];
}
- (void)showVerificationFailedWithError:(NSError *)error
{
[FingerprintViewScanController showVerificationFailedWithError:error
viewController:self
retryBlock:nil
cancelBlock:^{
// Do nothing.
}
2017-11-08 20:04:51 +01:00
tag:self.logTag];
}
2014-10-29 21:58:58 +01:00
#pragma mark - Action
- (void)closeButton
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)didTapShareButton
{
[self showSharingActivityWithCompletion:nil];
}
- (void)showScanner
{
FingerprintViewScanController *scanView = [FingerprintViewScanController new];
[scanView configureWithRecipientId:self.recipientId];
[self.navigationController pushViewController:scanView animated:YES];
}
- (void)learnMoreButtonTapped:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) {
2017-09-08 18:56:53 +02:00
NSString *learnMoreURL = @"https://support.signal.org/hc/en-us/articles/"
2017-06-09 17:56:37 +02:00
@"213134107";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:learnMoreURL]];
}
}
- (void)fingerprintLabelTapped:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) {
[self showSharingActivityWithCompletion:nil];
}
}
- (void)fingerprintViewTapped:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) {
[self showScanner];
}
}
- (void)verifyUnverifyButtonTapped:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateRecognized) {
[OWSPrimaryStorage.sharedManager.newDatabaseConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
BOOL isVerified = [[OWSIdentityManager sharedManager] verificationStateForRecipientId:self.recipientId
transaction:transaction]
== OWSVerificationStateVerified;
OWSVerificationState newVerificationState
= (isVerified ? OWSVerificationStateDefault : OWSVerificationStateVerified);
[[OWSIdentityManager sharedManager] setVerificationState:newVerificationState
identityKey:self.identityKey
recipientId:self.recipientId
isUserInitiatedChange:YES
transaction:transaction];
}];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
#pragma mark - Notifications
- (void)identityStateDidChange:(NSNotification *)notification
{
2017-12-19 17:38:25 +01:00
OWSAssertIsOnMainThread();
[self updateVerificationStateLabel];
}
2014-10-29 21:58:58 +01:00
@end
NS_ASSUME_NONNULL_END