diff --git a/Podfile.lock b/Podfile.lock index 5a05448bf..cbacb03af 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -136,7 +136,7 @@ CHECKOUT OPTIONS: :commit: 7054e4b13ee5bcd6d524adb6dc9a726e8c466308 :git: https://github.com/WhisperSystems/JSQMessagesViewController.git SignalServiceKit: - :commit: fba94754a6382b7fa55835ac25c7dd9931e594d8 + :commit: f2f654af194c9b232ccf2b3d382f8a096bffcd00 :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :commit: 877ac7438be3ad0b45ef5ca3969574e4b97112bf diff --git a/Signal/Images.xcassets/system_message_verified.imageset/Contents.json b/Signal/Images.xcassets/system_message_verified.imageset/Contents.json new file mode 100644 index 000000000..4d0f1d70a --- /dev/null +++ b/Signal/Images.xcassets/system_message_verified.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "system_message_verified@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "system_message_verified@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "system_message_verified@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@1x.png b/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@1x.png new file mode 100644 index 000000000..f7ef09c6a Binary files /dev/null and b/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@1x.png differ diff --git a/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@2x.png b/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@2x.png new file mode 100644 index 000000000..00fa432bd Binary files /dev/null and b/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@2x.png differ diff --git a/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@3x.png b/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@3x.png new file mode 100644 index 000000000..8894ba827 Binary files /dev/null and b/Signal/Images.xcassets/system_message_verified.imageset/system_message_verified@3x.png differ diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index 6aa08aaa7..80c2e5818 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -52,8 +52,6 @@ #import #import #import -#import -#import #import #import #import diff --git a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m index bfc72b68c..612f777ef 100644 --- a/Signal/src/ViewControllers/ConversationView/MessagesViewController.m +++ b/Signal/src/ViewControllers/ConversationView/MessagesViewController.m @@ -72,10 +72,9 @@ #import #import #import -#import -#import #import #import +#import #import #import #import @@ -1175,19 +1174,12 @@ typedef enum : NSUInteger { completion:completionHandler]; } -- (void)showFingerprintWithTheirIdentityKey:(NSData *)theirIdentityKey theirSignalId:(NSString *)theirSignalId +- (void)showFingerprintWithRecipientId:(NSString *)recipientId { // Ensure keyboard isn't hiding the "safety numbers changed" interaction when we // return from FingerprintViewController. [self dismissKeyBoard]; - OWSFingerprintBuilder *builder = - [[OWSFingerprintBuilder alloc] initWithStorageManager:self.storageManager contactsManager:self.contactsManager]; - OWSFingerprint *fingerprint = - [builder fingerprintWithTheirSignalId:theirSignalId theirIdentityKey:theirIdentityKey]; - - NSString *contactName = [self.contactsManager displayNameForPhoneIdentifier:theirSignalId]; - UIViewController *viewController = [[UIStoryboard main] instantiateViewControllerWithIdentifier:@"FingerprintViewController"]; if (![viewController isKindOfClass:[FingerprintViewController class]]) { @@ -1196,8 +1188,7 @@ typedef enum : NSUInteger { return; } FingerprintViewController *fingerprintViewController = (FingerprintViewController *)viewController; - - [fingerprintViewController configureWithFingerprint:fingerprint contactName:contactName]; + [fingerprintViewController configureWithRecipientId:recipientId]; [self presentViewController:fingerprintViewController animated:YES completion:nil]; } @@ -2248,18 +2239,7 @@ typedef enum : NSUInteger { { NSParameterAssert(signalId != nil); - OWSFingerprintBuilder *fingerprintBuilder = - [[OWSFingerprintBuilder alloc] initWithStorageManager:self.storageManager contactsManager:self.contactsManager]; - - OWSFingerprint *fingerprint = [fingerprintBuilder fingerprintWithTheirSignalId:signalId]; - - FingerprintViewController *fingerprintViewController = - [[UIStoryboard main] instantiateViewControllerWithIdentifier:@"FingerprintViewController"]; - - NSString *contactName = [self.contactsManager displayNameForPhoneIdentifier:signalId]; - [fingerprintViewController configureWithFingerprint:fingerprint contactName:contactName]; - - [self presentViewController:fingerprintViewController animated:YES completion:nil]; + [self showFingerprintWithRecipientId:signalId]; } - (void)handleInfoMessageTap:(TSInfoMessage *)message @@ -2286,6 +2266,9 @@ typedef enum : NSUInteger { case TSInfoMessageTypeDisappearingMessagesUpdate: [self showConversationSettings]; return; + case TSInfoMessageVerificationStateChange: + [self showFingerprintWithRecipientId:((OWSVerificationStateChangeMessage *)message).recipientId]; + break; } DDLogInfo(@"%@ Unhandled tap for info message:%@", self.tag, message); @@ -2345,8 +2328,7 @@ typedef enum : NSUInteger { style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) { DDLogInfo(@"%@ Remote Key Changed actions: Show fingerprint display", self.tag); - [self showFingerprintWithTheirIdentityKey:errorMessage.newIdentityKey - theirSignalId:errorMessage.theirSignalId]; + [self showFingerprintWithRecipientId:errorMessage.theirSignalId]; }]; [actionSheetController addAction:showSafteyNumberAction]; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index d4b485976..934bd8460 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -10,6 +10,7 @@ #import #import #import +#import #import #import #import @@ -640,6 +641,39 @@ NS_ASSUME_NONNULL_BEGIN inThread:thread messageType:TSInfoMessageTypeGroupQuit]]; + [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + thread:thread + recipientId:@"+19174054215" + verificationState:OWSVerificationStateDefault + isLocalChange:YES]]; + [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + thread:thread + recipientId:@"+19174054215" + verificationState:OWSVerificationStateVerified + isLocalChange:YES]]; + [result + addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + thread:thread + recipientId:@"+19174054215" + verificationState:OWSVerificationStateNoLongerVerified + isLocalChange:YES]]; + [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + thread:thread + recipientId:@"+19174054215" + verificationState:OWSVerificationStateDefault + isLocalChange:NO]]; + [result addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + thread:thread + recipientId:@"+19174054215" + verificationState:OWSVerificationStateVerified + isLocalChange:NO]]; + [result + addObject:[[OWSVerificationStateChangeMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + thread:thread + recipientId:@"+19174054215" + verificationState:OWSVerificationStateNoLongerVerified + isLocalChange:NO]]; + [result addObject:[TSErrorMessage missingSessionWithEnvelope:[self createEnvelopeForThread:thread] withTransaction:transaction]]; [result addObject:[TSErrorMessage invalidKeyExceptionWithEnvelope:[self createEnvelopeForThread:thread] diff --git a/Signal/src/ViewControllers/FingerprintViewController.h b/Signal/src/ViewControllers/FingerprintViewController.h index 826650e6f..ef8ab4efe 100644 --- a/Signal/src/ViewControllers/FingerprintViewController.h +++ b/Signal/src/ViewControllers/FingerprintViewController.h @@ -13,8 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nullable) OWSConversationSettingsTableViewController *dismissDelegate; -- (void)configureWithFingerprint:(OWSFingerprint *)fingerprint - contactName:(NSString *)contactName NS_SWIFT_NAME(configure(fingerprint:contactName:)); +- (void)configureWithRecipientId:(NSString *)recipientId NS_SWIFT_NAME(configure(recipientId:)); - (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data; diff --git a/Signal/src/ViewControllers/FingerprintViewController.m b/Signal/src/ViewControllers/FingerprintViewController.m index 372bf25f1..fc51c7a8c 100644 --- a/Signal/src/ViewControllers/FingerprintViewController.m +++ b/Signal/src/ViewControllers/FingerprintViewController.m @@ -11,6 +11,7 @@ #import #import #import +#import #import #import #import @@ -20,34 +21,46 @@ NS_ASSUME_NONNULL_BEGIN @interface FingerprintViewController () -@property (strong, nonatomic) TSStorageManager *storageManager; -@property (strong, nonatomic) OWSFingerprint *fingerprint; -@property (strong, nonatomic) NSString *contactName; -@property (strong, nonatomic) OWSQRCodeScanningViewController *qrScanningController; - -@property (strong, nonatomic) IBOutlet UINavigationBar *modalNavigationBar; -@property (strong, nonatomic) IBOutlet UIBarButtonItem *dismissModalButton; -@property (strong, nonatomic) IBOutlet UIBarButtonItem *shareButton; -@property (strong, nonatomic) IBOutlet UIView *qrScanningView; -@property (strong, nonatomic) IBOutlet UILabel *scanningInstructions; -@property (strong, nonatomic) IBOutlet UIView *scanningContainer; -@property (strong, nonatomic) IBOutlet UIView *instructionsContainer; -@property (strong, nonatomic) IBOutlet UIView *qrContainer; -@property (strong, nonatomic) IBOutlet UIView *privacyVerificationQRCodeFrame; -@property (strong, nonatomic) IBOutlet UIImageView *privacyVerificationQRCode; -@property (strong, nonatomic) IBOutlet OWSHighlightableLabel *privacyVerificationFingerprint; -@property (strong, nonatomic) IBOutlet UILabel *instructionsLabel; -@property (strong, nonatomic) IBOutlet UIButton *scanButton; +@property (nonatomic) TSStorageManager *storageManager; +@property (nonatomic) OWSFingerprint *fingerprint; +@property (nonatomic) NSString *contactName; +@property (nonatomic) OWSQRCodeScanningViewController *qrScanningController; + +@property (nonatomic) IBOutlet UINavigationBar *modalNavigationBar; +@property (nonatomic) IBOutlet UIBarButtonItem *dismissModalButton; +@property (nonatomic) IBOutlet UIBarButtonItem *shareButton; +@property (nonatomic) IBOutlet UIView *qrScanningView; +@property (nonatomic) IBOutlet UILabel *scanningInstructions; +@property (nonatomic) IBOutlet UIView *scanningContainer; +@property (nonatomic) IBOutlet UIView *instructionsContainer; +@property (nonatomic) IBOutlet UIView *qrContainer; +@property (nonatomic) IBOutlet UIView *privacyVerificationQRCodeFrame; +@property (nonatomic) IBOutlet UIImageView *privacyVerificationQRCode; +@property (nonatomic) IBOutlet OWSHighlightableLabel *privacyVerificationFingerprint; +@property (nonatomic) IBOutlet UILabel *instructionsLabel; +@property (nonatomic) IBOutlet UIButton *scanButton; @end @implementation FingerprintViewController -- (void)configureWithFingerprint:(OWSFingerprint *)fingerprint - contactName:(NSString *)contactName +- (void)configureWithRecipientId:(NSString *)recipientId { - self.fingerprint = fingerprint; - self.contactName = contactName; + OWSAssert(recipientId.length > 0); + + self.storageManager = [TSStorageManager sharedManager]; + + OWSContactsManager *contactsManager = [Environment getCurrent].contactsManager; + self.contactName = [contactsManager displayNameForPhoneIdentifier:recipientId]; + + OWSRecipientIdentity *_Nullable recipientIdentity = + [[OWSIdentityManager sharedManager] recipientIdentityForRecipientId:recipientId]; + OWSAssert(recipientIdentity); + + OWSFingerprintBuilder *builder = + [[OWSFingerprintBuilder alloc] initWithStorageManager:self.storageManager contactsManager:contactsManager]; + self.fingerprint = + [builder fingerprintWithTheirSignalId:recipientId theirIdentityKey:recipientIdentity.identityKey]; } - (void)viewDidLoad @@ -63,8 +76,6 @@ NS_ASSUME_NONNULL_BEGIN self.modalNavigationBar.shadowImage = [UIImage new]; self.modalNavigationBar.translucent = YES; - self.storageManager = [TSStorageManager sharedManager]; - // HACK to get full width preview layer CGRect oldFrame = self.qrScanningView.frame; CGRect newFrame = CGRectMake(oldFrame.origin.x, diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m index de1268867..c0eb88322 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m @@ -21,8 +21,6 @@ #import #import #import -#import -#import #import #import #import @@ -226,18 +224,8 @@ NS_ASSUME_NONNULL_BEGIN } FingerprintViewController *fingerprintViewController = [[UIStoryboard main] instantiateViewControllerWithIdentifier:@"FingerprintViewController"]; - - OWSFingerprintBuilder *fingerprintBuilder = - [[OWSFingerprintBuilder alloc] initWithStorageManager:strongSelf.storageManager - contactsManager:strongSelf.contactsManager]; - - OWSFingerprint *fingerprint = - [fingerprintBuilder fingerprintWithTheirSignalId:strongSelf.thread.contactIdentifier]; - - [fingerprintViewController configureWithFingerprint:fingerprint - contactName:[strongSelf threadName]]; + [fingerprintViewController configureWithRecipientId:strongSelf.thread.contactIdentifier]; fingerprintViewController.dismissDelegate = strongSelf; - [strongSelf presentViewController:fingerprintViewController animated:YES completion:nil]; }]]; } diff --git a/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift b/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift index 0836ec4ac..ab3d8fe38 100644 --- a/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift +++ b/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift @@ -84,10 +84,7 @@ class SafetyNumberConfirmationAlert: NSObject { public func presentSafetyNumberViewController(theirIdentityKey: Data, theirRecipientId: String, theirDisplayName: String, completion: (() -> Void)? = nil) { let fingerprintViewController = UIStoryboard.instantiateFingerprintViewController() - let fingerprintBuilder = OWSFingerprintBuilder(storageManager: self.storageManager, contactsManager: self.contactsManager) - let fingerprint = fingerprintBuilder.fingerprint(withTheirSignalId: theirRecipientId, theirIdentityKey: theirIdentityKey) - - fingerprintViewController.configure(fingerprint: fingerprint, contactName: theirDisplayName) + fingerprintViewController.configure(recipientId: theirRecipientId) UIApplication.shared.frontmostViewController?.present(fingerprintViewController, animated: true, completion: completion) } diff --git a/Signal/src/views/OWSSystemMessageCell.m b/Signal/src/views/OWSSystemMessageCell.m index 21916ee6d..1d342d0f1 100644 --- a/Signal/src/views/OWSSystemMessageCell.m +++ b/Signal/src/views/OWSSystemMessageCell.m @@ -3,12 +3,15 @@ // #import "OWSSystemMessageCell.h" +#import "Environment.h" #import "NSBundle+JSQMessages.h" +#import "OWSContactsManager.h" #import "TSUnreadIndicatorInteraction.h" #import "UIColor+OWS.h" #import "UIFont+OWS.h" #import "UIView+OWS.h" #import +#import #import #import #import @@ -81,13 +84,13 @@ UIImage *icon = [self iconForInteraction:self.interaction]; self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; self.imageView.tintColor = [self iconColorForInteraction:self.interaction]; - self.titleLabel.text = [OWSSystemMessageCell titleForInteraction:self.interaction]; - self.titleLabel.textColor = [self textColorForInteraction:self.interaction]; + self.titleLabel.textColor = [OWSSystemMessageCell textColor]; + [OWSSystemMessageCell applyTitleForInteraction:self.interaction label:self.titleLabel]; [self setNeedsLayout]; } -- (UIColor *)textColorForInteraction:(TSInteraction *)interaction ++ (UIColor *)textColor { return [UIColor colorWithRGBHex:0x303030]; } @@ -134,6 +137,9 @@ case TSInfoMessageTypeDisappearingMessagesUpdate: result = [UIImage imageNamed:@"system_message_timer"]; break; + case TSInfoMessageVerificationStateChange: + result = [UIImage imageNamed:@"system_message_verified"]; + break; } } else if ([interaction isKindOfClass:[TSCall class]]) { result = [UIImage imageNamed:@"system_message_call"]; @@ -145,19 +151,66 @@ return result; } -+ (NSString *)titleForInteraction:(TSInteraction *)interaction ++ (void)applyTitleForInteraction:(TSInteraction *)interaction label:(UILabel *)label { + OWSAssert(interaction); + OWSAssert(label); + // TODO: Should we move the copy generation into this view? if ([interaction isKindOfClass:[TSErrorMessage class]]) { - return interaction.description; + label.text = interaction.description; } else if ([interaction isKindOfClass:[TSInfoMessage class]]) { - return interaction.description; + if ([interaction isKindOfClass:[OWSVerificationStateChangeMessage class]]) { + OWSVerificationStateChangeMessage *message = (OWSVerificationStateChangeMessage *)interaction; + UIFont *titleFont = [self titleFont]; + NSMutableAttributedString *title = [NSMutableAttributedString new]; + BOOL isVerified = message.verificationState == OWSVerificationStateVerified; + // if (isVerified) { + // [title + // appendAttributedString:[[NSAttributedString alloc] + // initWithString:@"\uf00c " + // attributes:@{ + // NSFontAttributeName : [UIFont + // ows_fontAwesomeFont:titleFont.pointSize], + // NSForegroundColorAttributeName : [UIColor + // ows_signalBrandBlueColor], + //// NSBaselineOffsetAttributeName : @(-1.f), + // }]]; + // } + + NSString *displayName = + [[Environment getCurrent].contactsManager displayNameForPhoneIdentifier:message.recipientId]; + NSString *titleFormat = (isVerified + ? (message.isLocalChange + ? NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_LOCAL", + @"Format for info message indicating that the verification state was verified on " + @"this device. Embeds {{user's name or phone number}}.") + : NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_OTHER_DEVICE", + @"Format for info message indicating that the verification state was verified on " + @"another device. Embeds {{user's name or phone number}}.")) + : (message.isLocalChange + ? NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_LOCAL", + @"Format for info message indicating that the verification state was unverified on " + @"this device. Embeds {{user's name or phone number}}.") + : NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_OTHER_DEVICE", + @"Format for info message indicating that the verification state was unverified on " + @"another device. Embeds {{user's name or phone number}}."))); + [title appendAttributedString:[[NSAttributedString alloc] + initWithString:[NSString stringWithFormat:titleFormat, displayName] + attributes:@{ + NSFontAttributeName : titleFont, + NSForegroundColorAttributeName : [self textColor], + }]]; + label.attributedText = title; + } else { + label.text = interaction.description; + } } else if ([interaction isKindOfClass:[TSCall class]]) { - return interaction.description; + label.text = interaction.description; } else { OWSFail(@"Unknown interaction type: %@", [interaction class]); - return nil; + label.text = nil; } } @@ -216,13 +269,11 @@ result.height += self.topVMargin; result.height += self.bottomVMargin; - NSString *title = [self titleForInteraction:interaction]; - // Creating a UILabel to measure the layout is expensive, but it's the only // reliable way to do it. UILabel *label = [UILabel new]; label.font = [self titleFont]; - label.text = title; + [OWSSystemMessageCell applyTitleForInteraction:interaction label:label]; label.numberOfLines = 0; label.lineBreakMode = NSLineBreakByWordWrapping; CGFloat maxTitleWidth = (collectionViewWidth - ([self hMargin] * 2.f + [self hSpacing] + [self iconSize])); diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index c2d7adf4b..0f02212e4 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1390,6 +1390,18 @@ /* Label indicating the phone number currently being verified. */ "VERIFICATION_PHONE_NUMBER_FORMAT" = "Enter the verification code we sent to %@."; +/* Format for info message indicating that the verification state was unverified on this device. Embeds {{user's name or phone number}}. */ +"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_LOCAL" = "You unverified %@."; + +/* Format for info message indicating that the verification state was unverified on another device. Embeds {{user's name or phone number}}. */ +"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_OTHER_DEVICE" = "You unverified %@ on another device."; + +/* Format for info message indicating that the verification state was verified on this device. Embeds {{user's name or phone number}}. */ +"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_LOCAL" = "You verified %@."; + +/* Format for info message indicating that the verification state was verified on another device. Embeds {{user's name or phone number}}. */ +"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_OTHER_DEVICE" = "You verified %@ on another device."; + /* Action sheet item table cell label in conversation settings */ "VERIFY_PRIVACY" = "Show Safety Number";