New Fingerprint Format

Rather than verifying eachothers keys separately, you now verify the
privacy with your recipient by sharing a single composite number or
QRCode.

This is a breaking change, in coordination with Desktop and Android.

UX
--

Fingeprint is no longer in line with identity key error. Instead you
have the option of going to the full-screen safety number verification
experience.

Overhauled fingerprint design
-----------------------------

* use same modal dismiss button as elsewhere
* remove fingerprint from settings.
* quick slide in animation vs slow fade
  * existing was painfully slow
  * blur effect is better metaphor for something slide over top
  * anyway there was a rendering glitch in the end of fade where
    underlying navbar would "snap" out

Also Fixed
----------
Always provide a name string for contact

* Centralize all the nil-checking
* Fall back to "unknown contact"

allow multi-line error messages

// FREEBIE
This commit is contained in:
Michael Kirk 2016-09-11 16:53:12 -04:00 committed by Michael Kirk
parent cc2a25b184
commit 11a586a835
26 changed files with 768 additions and 918 deletions

View File

@ -3,12 +3,13 @@ source 'https://github.com/CocoaPods/Specs.git'
target 'Signal' do
pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git'
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git'
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'new-fingerprint-format'
#pod 'SignalServiceKit', path: '../SignalServiceKit'
pod 'OpenSSL', '~> 1.0.208'
pod 'PastelogKit', '~> 1.3'
pod 'FFCircularProgressView', '~> 0.5'
pod 'SCWaveformView', '~> 1.0'
pod 'ZXingObjC'
pod 'DJWActionSheet'
pod 'JSQMessagesViewController'
target 'SignalTests' do

View File

@ -111,6 +111,9 @@ PODS:
- YapDatabase/SQLCipher/Core
- YapDatabase/SQLCipher/Extensions/Views (2.9.2):
- YapDatabase/SQLCipher/Core
- ZXingObjC (3.1.0):
- ZXingObjC/All (= 3.1.0)
- ZXingObjC/All (3.1.0)
DEPENDENCIES:
- DJWActionSheet
@ -119,18 +122,20 @@ DEPENDENCIES:
- OpenSSL (~> 1.0.208)
- PastelogKit (~> 1.3)
- SCWaveformView (~> 1.0)
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`)
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `new-fingerprint-format`)
- SocketRocket (from `https://github.com/facebook/SocketRocket.git`)
- ZXingObjC
EXTERNAL SOURCES:
SignalServiceKit:
:branch: new-fingerprint-format
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:git: https://github.com/facebook/SocketRocket.git
CHECKOUT OPTIONS:
SignalServiceKit:
:commit: ce1aa04b6193b47b82b44d529cca12c62fae4c48
:commit: 2b612e9ab72b427b310b125b1dc82eef1d3f85ec
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:commit: 41b57bb2fc292a814f758441a05243eb38457027
@ -160,7 +165,8 @@ SPEC CHECKSUMS:
TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c
UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d
YapDatabase: b1e43555a34a5298e23a045be96817a5ef0da58f
ZXingObjC: bf15b3814f7a105b6d99f47da2333c93a063650a
PODFILE CHECKSUM: 5dccee4c1c1ba5d4bf9575a81eeede82d1e89e8b
PODFILE CHECKSUM: d4204cf787649f9512dc74e5844f48d2570d5b2e
COCOAPODS: 1.0.1

View File

@ -159,8 +159,6 @@
A547DD741A70A87800103EC7 /* DJWActionSheet+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = A547DD721A70A87800103EC7 /* DJWActionSheet+OWS.m */; };
A5509ECA1A69AB8B00ABA4BC /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A5509EC91A69AB8B00ABA4BC /* Storyboard.storyboard */; };
A5509ECD1A69B1D600ABA4BC /* CountryCodeTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A5509ECC1A69B1D600ABA4BC /* CountryCodeTableViewCell.m */; };
A56977911A351BC400173BF2 /* ScanIdentityBarcodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A569778E1A351BC400173BF2 /* ScanIdentityBarcodeViewController.m */; };
A56977921A351BC400173BF2 /* PresentIdentityQRCodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A569778F1A351BC400173BF2 /* PresentIdentityQRCodeViewController.m */; };
A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */; };
A5E9D4BB1A65FAD800E4481C /* TSVideoAttachmentAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = A5E9D4B91A65FAD800E4481C /* TSVideoAttachmentAdapter.m */; };
AD41D7B51A6F6F0600241130 /* play_button.png in Resources */ = {isa = PBXBuildFile; fileRef = AD41D7B31A6F6F0600241130 /* play_button.png */; };
@ -789,10 +787,6 @@
A5509EC91A69AB8B00ABA4BC /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Storyboard.storyboard; path = Storyboard/Storyboard.storyboard; sourceTree = "<group>"; };
A5509ECB1A69B1D600ABA4BC /* CountryCodeTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryCodeTableViewCell.h; sourceTree = "<group>"; };
A5509ECC1A69B1D600ABA4BC /* CountryCodeTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryCodeTableViewCell.m; sourceTree = "<group>"; };
A569778D1A351BC400173BF2 /* ScanIdentityBarcodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScanIdentityBarcodeViewController.h; sourceTree = "<group>"; };
A569778E1A351BC400173BF2 /* ScanIdentityBarcodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScanIdentityBarcodeViewController.m; sourceTree = "<group>"; };
A569778F1A351BC400173BF2 /* PresentIdentityQRCodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PresentIdentityQRCodeViewController.m; sourceTree = "<group>"; };
A56977901A351BC400173BF2 /* PresentIdentityQRCodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PresentIdentityQRCodeViewController.h; sourceTree = "<group>"; };
A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ShowGroupMembersViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
A5D0699A1A50E9CB004CB540 /* ShowGroupMembersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowGroupMembersViewController.h; sourceTree = "<group>"; };
A5E9D4B91A65FAD800E4481C /* TSVideoAttachmentAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSVideoAttachmentAdapter.m; sourceTree = "<group>"; };
@ -2301,10 +2295,6 @@
FC3196321A08142D0094C78E /* Signals */ = {
isa = PBXGroup;
children = (
A569778D1A351BC400173BF2 /* ScanIdentityBarcodeViewController.h */,
A569778E1A351BC400173BF2 /* ScanIdentityBarcodeViewController.m */,
A56977901A351BC400173BF2 /* PresentIdentityQRCodeViewController.h */,
A569778F1A351BC400173BF2 /* PresentIdentityQRCodeViewController.m */,
FC3196281A067D8F0094C78E /* MessageComposeTableViewController.h */,
FC3196291A067D8F0094C78E /* MessageComposeTableViewController.m */,
FCAC963A19FEF9280046DFC5 /* SignalsViewController.h */,
@ -2688,7 +2678,6 @@
A5509ECD1A69B1D600ABA4BC /* CountryCodeTableViewCell.m in Sources */,
76EB05F618170B33006006FC /* CallConnectUtil.m in Sources */,
76EB061218170B33006006FC /* LoggingUtil.m in Sources */,
A56977921A351BC400173BF2 /* PresentIdentityQRCodeViewController.m in Sources */,
76EB060E18170B33006006FC /* DecayingSampleEstimator.m in Sources */,
76EB05BA18170B33006006FC /* CommitPacket.m in Sources */,
76EB060218170B33006006FC /* InitiatorSessionDescriptor.m in Sources */,
@ -2719,7 +2708,6 @@
FCD274EB1A5AFDDB00202277 /* AboutTableViewController.m in Sources */,
E197B61618BBEC1A00F073E5 /* StretchFactorController.m in Sources */,
FCFD257F1A154B2C00F4C644 /* RegistrationViewController.m in Sources */,
A56977911A351BC400173BF2 /* ScanIdentityBarcodeViewController.m in Sources */,
701231B518ECAA4500D456C4 /* EvpMessageDigest.m in Sources */,
76EB062218170B33006006FC /* CyclicalBuffer.m in Sources */,
76EB063C18170B33006006FC /* NumberUtil.m in Sources */,

View File

@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.5.3</string>
<string>2.6.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -38,7 +38,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>2.5.3.4</string>
<string>2.6.0.1</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LOGS_EMAIL</key>

View File

@ -40,13 +40,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self setupAppearance];
[[PushManager sharedManager] registerPushKitNotificationFuture];
if (getenv("runningTests_dontStartApp")) {
return YES;
}
// Initializing logger
CategorizingLogger *logger = [CategorizingLogger categorizingLogger];
[logger addLoggingCallback:^(NSString *category, id details, NSUInteger index){
@ -55,6 +48,13 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
// Setting up environment
[Environment setCurrent:[Release releaseEnvironmentWithLogging:logger]];
[self setupAppearance];
[[PushManager sharedManager] registerPushKitNotificationFuture];
if (getenv("runningTests_dontStartApp")) {
return YES;
}
if ([TSAccountManager isRegistered]) {
[Environment.getCurrent.contactsManager doAfterEnvironmentInitSetup];
}

View File

@ -25,9 +25,9 @@
TSMessageAdapter *message = (TSMessageAdapter *)messageData;
if (message.messageType == TSInfoMessageAdapter || message.messageType == TSErrorMessageAdapter) {
// Prevent cropping message text by accounting for message container/icon
superSize.height = OWSDisplayedMessageCellHeight;
// But also allow for multi-line error messages.
superSize.height = fmax(superSize.height, OWSDisplayedMessageCellHeight);
}
return superSize;

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="tuk-0x-yCb">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="tuk-0x-yCb">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
@ -114,7 +114,7 @@
</barButtonItem>
</navigationItem>
<connections>
<segue destination="urv-62-RsD" kind="modal" identifier="fingerprintSegue" animates="NO" modalPresentationStyle="overCurrentContext" id="Zjl-QX-tHE"/>
<segue destination="urv-62-RsD" kind="modal" identifier="fingerprintSegue" modalPresentationStyle="overCurrentContext" modalTransitionStyle="coverVertical" id="Zjl-QX-tHE"/>
<segue destination="bDi-2Q-XOC" kind="push" identifier="updateGroupSegue" id="gZ1-lh-srF"/>
<segue destination="JeZ-9g-U61" kind="push" identifier="showGroupMembersSegue" id="Gc6-AD-JV1"/>
</connections>
@ -135,236 +135,184 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView opaque="NO" contentMode="left" translatesAutoresizingMaskIntoConstraints="NO" id="VCu-vN-Pjg">
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="X9c-nY-Re0">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="kEV-0h-NsX">
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="GUy-oN-Va6">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zFI-eF-Feb" userLabel="Their Fingerprint">
<rect key="frame" x="0.0" y="0.0" width="400" height="221"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AwQ-ec-WBO">
<rect key="frame" x="0.0" y="220" width="400" height="1"/>
<color key="backgroundColor" white="0.61676562499999998" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="yMD-Sw-sDy"/>
</constraints>
</view>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="btnCamera--white" translatesAutoresizingMaskIntoConstraints="NO" id="HyV-ht-MrM">
<rect key="frame" x="19" y="73" width="75" height="75"/>
<constraints>
<constraint firstAttribute="height" constant="75" id="Yxr-T5-Jck"/>
<constraint firstAttribute="width" constant="75" id="bRq-v9-94p"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Momo's Fingerprint" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ii9-7N-hCc">
<rect key="frame" x="115" y="35" width="250" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="epx-el-0T8"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="4" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kBW-ix-mdQ">
<rect key="frame" x="115" y="73" width="250" height="75"/>
<constraints>
<constraint firstAttribute="height" constant="75" id="jaw-8F-c1C"/>
</constraints>
<string key="text">A0 09 9A FF A8 8A 09 99
1E 5C 7B 8A 56 2B 1F 65
B3 83 8D 1D D0 FA 9C E3
A0 09 9A FF A8 8A 09 99</string>
<fontDescription key="fontDescription" name="Courier" family="Courier" pointSize="16"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tap to scan another user's fingerprint" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ge0-8K-A4F">
<rect key="frame" x="115" y="160" width="213" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="HyF-Kt-KmB"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<color key="textColor" white="0.75" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="trailing" secondItem="HyV-ht-MrM" secondAttribute="trailing" constant="306" id="1XO-7v-R7T"/>
<constraint firstAttribute="height" constant="221" id="HBq-Ly-0qj"/>
<constraint firstItem="AwQ-ec-WBO" firstAttribute="leading" secondItem="zFI-eF-Feb" secondAttribute="leading" id="HfI-Gb-ykL"/>
<constraint firstItem="ge0-8K-A4F" firstAttribute="leading" secondItem="zFI-eF-Feb" secondAttribute="leading" constant="115" id="Io8-J3-g1q"/>
<constraint firstAttribute="trailing" secondItem="AwQ-ec-WBO" secondAttribute="trailing" id="LGK-WL-NBq"/>
<constraint firstItem="AwQ-ec-WBO" firstAttribute="top" secondItem="zFI-eF-Feb" secondAttribute="top" constant="220" id="ML3-4p-6ML"/>
<constraint firstItem="ge0-8K-A4F" firstAttribute="top" secondItem="zFI-eF-Feb" secondAttribute="top" constant="160" id="MR1-lO-oHk"/>
<constraint firstItem="HyV-ht-MrM" firstAttribute="top" secondItem="zFI-eF-Feb" secondAttribute="top" constant="73" id="Wyq-lm-CaG"/>
<constraint firstItem="Ii9-7N-hCc" firstAttribute="top" secondItem="zFI-eF-Feb" secondAttribute="top" constant="35" id="Zd1-Oz-JmD"/>
<constraint firstItem="HyV-ht-MrM" firstAttribute="leading" secondItem="zFI-eF-Feb" secondAttribute="leading" constant="19" id="dgg-gQ-GPY"/>
<constraint firstItem="kBW-ix-mdQ" firstAttribute="leading" secondItem="zFI-eF-Feb" secondAttribute="leading" constant="115" id="fYR-kP-vRN"/>
<constraint firstAttribute="trailing" secondItem="kBW-ix-mdQ" secondAttribute="trailing" constant="35" id="jVG-sA-F3W"/>
<constraint firstItem="Ii9-7N-hCc" firstAttribute="leading" secondItem="zFI-eF-Feb" secondAttribute="leading" constant="115" id="nrS-05-Uru"/>
<constraint firstItem="kBW-ix-mdQ" firstAttribute="top" secondItem="zFI-eF-Feb" secondAttribute="top" constant="73" id="o0C-aS-9NZ"/>
<constraint firstAttribute="trailing" secondItem="Ii9-7N-hCc" secondAttribute="trailing" constant="35" id="vKr-Nz-ISB"/>
<constraint firstAttribute="trailing" secondItem="ge0-8K-A4F" secondAttribute="trailing" constant="72" id="xBr-5Q-660"/>
</constraints>
<connections>
<outletCollection property="gestureRecognizers" destination="fRl-Lt-y39" appends="YES" id="wIa-Aj-hqy"/>
</connections>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Compare both fingerprints to verify your contact's identity and the integrity of the message." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DV4-GV-ZPf">
<rect key="frame" x="22" y="460" width="356" height="44"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="bif-t1-qQO"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="B4o-Rc-z68" userLabel="My Fingerprint">
<rect key="frame" x="0.0" y="221" width="400" height="221"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WhZ-e0-THb">
<rect key="frame" x="0.0" y="220" width="400" height="1"/>
<color key="backgroundColor" white="0.61676562499999998" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="XgI-5W-uMZ"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your Fingerprint" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5De-Jq-XWv">
<rect key="frame" x="115" y="35" width="250" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="475-f9-1TO"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="btnQRShow--white" highlightedImage="btnQRShow--white-1" translatesAutoresizingMaskIntoConstraints="NO" id="YOs-e5-ee3">
<rect key="frame" x="19" y="73" width="75" height="75"/>
<constraints>
<constraint firstAttribute="width" constant="75" id="5IR-xd-6uL"/>
<constraint firstAttribute="height" constant="75" id="Nfl-jf-HzC"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tap to display your fingerprint for another user" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9Wg-UP-VDC">
<rect key="frame" x="115" y="160" width="208" height="43"/>
<constraints>
<constraint firstAttribute="height" constant="43" id="xb1-B9-MyR"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<color key="textColor" white="0.75" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="4" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e7E-iS-3Oc">
<rect key="frame" x="115" y="73" width="250" height="75"/>
<constraints>
<constraint firstAttribute="height" constant="75" id="k9r-Fg-1QJ"/>
</constraints>
<string key="text">A0 09 9A FF A8 8A 09 99
1E 5C 7B 8A 56 2B 1F 65
B3 83 8D 1D D0 FA 9C E3
A0 09 9A FF A8 8A 09 99</string>
<fontDescription key="fontDescription" name="Courier" family="Courier" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstItem="9Wg-UP-VDC" firstAttribute="leading" secondItem="B4o-Rc-z68" secondAttribute="leading" constant="115" id="3zq-WB-ASq"/>
<constraint firstAttribute="trailing" secondItem="5De-Jq-XWv" secondAttribute="trailing" constant="35" id="8wL-8Y-SSo"/>
<constraint firstAttribute="trailing" secondItem="e7E-iS-3Oc" secondAttribute="trailing" constant="35" id="CbM-nY-P0U"/>
<constraint firstItem="9Wg-UP-VDC" firstAttribute="top" secondItem="B4o-Rc-z68" secondAttribute="top" constant="160" id="DDh-3a-uIm"/>
<constraint firstItem="WhZ-e0-THb" firstAttribute="leading" secondItem="B4o-Rc-z68" secondAttribute="leading" id="EIo-e5-i5c"/>
<constraint firstAttribute="trailing" secondItem="WhZ-e0-THb" secondAttribute="trailing" id="Isj-Bf-lu6"/>
<constraint firstItem="5De-Jq-XWv" firstAttribute="leading" secondItem="B4o-Rc-z68" secondAttribute="leading" constant="115" id="RmR-Hh-1Ys"/>
<constraint firstItem="YOs-e5-ee3" firstAttribute="top" secondItem="B4o-Rc-z68" secondAttribute="top" constant="73" id="VaC-nb-QHZ"/>
<constraint firstAttribute="height" constant="221" id="a12-JK-RBy"/>
<constraint firstItem="e7E-iS-3Oc" firstAttribute="leading" secondItem="B4o-Rc-z68" secondAttribute="leading" constant="115" id="clq-cu-8Hq"/>
<constraint firstItem="WhZ-e0-THb" firstAttribute="top" secondItem="B4o-Rc-z68" secondAttribute="top" constant="220" id="fDt-eB-0Lh"/>
<constraint firstAttribute="trailing" secondItem="YOs-e5-ee3" secondAttribute="trailing" constant="306" id="flI-Gp-aWH"/>
<constraint firstAttribute="trailing" secondItem="9Wg-UP-VDC" secondAttribute="trailing" constant="77" id="mLc-13-Wcv"/>
<constraint firstItem="YOs-e5-ee3" firstAttribute="leading" secondItem="B4o-Rc-z68" secondAttribute="leading" constant="19" id="md2-eV-aBb"/>
<constraint firstItem="e7E-iS-3Oc" firstAttribute="top" secondItem="B4o-Rc-z68" secondAttribute="top" constant="73" id="mif-35-C1B"/>
<constraint firstItem="5De-Jq-XWv" firstAttribute="top" secondItem="B4o-Rc-z68" secondAttribute="top" constant="35" id="pCa-lD-NlC"/>
</constraints>
<connections>
<outletCollection property="gestureRecognizers" destination="Mg3-ad-PGG" appends="YES" id="fDZ-ZY-J04"/>
</connections>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Yoz-ex-1gK">
<rect key="frame" x="19" y="492" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="Ie5-wL-XEo"/>
<constraint firstAttribute="height" constant="50" id="aq2-hO-WY0"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="60"/>
<state key="normal" title="×">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="closeButtonAction:" destination="urv-62-RsD" eventType="touchUpInside" id="m15-1K-9gB"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="Yoz-ex-1gK" secondAttribute="bottom" constant="26" id="EVw-Jr-67j"/>
<constraint firstAttribute="trailing" secondItem="zFI-eF-Feb" secondAttribute="trailing" id="Jfg-Ac-5SC"/>
<constraint firstItem="zFI-eF-Feb" firstAttribute="leading" secondItem="kEV-0h-NsX" secondAttribute="leading" id="Lh4-K4-lK4"/>
<constraint firstItem="B4o-Rc-z68" firstAttribute="leading" secondItem="kEV-0h-NsX" secondAttribute="leading" id="Pe1-Ko-Er7"/>
<constraint firstItem="Yoz-ex-1gK" firstAttribute="leading" secondItem="kEV-0h-NsX" secondAttribute="leading" constant="19" id="Ptp-Na-ekf"/>
<constraint firstItem="zFI-eF-Feb" firstAttribute="top" secondItem="kEV-0h-NsX" secondAttribute="top" id="cBU-ru-0Nv"/>
<constraint firstItem="DV4-GV-ZPf" firstAttribute="leading" secondItem="kEV-0h-NsX" secondAttribute="leading" constant="22" id="eVX-2q-T74"/>
<constraint firstAttribute="trailing" secondItem="B4o-Rc-z68" secondAttribute="trailing" id="iKt-RZ-COk"/>
<constraint firstAttribute="trailing" secondItem="DV4-GV-ZPf" secondAttribute="trailing" constant="22" id="qbq-AK-HjD"/>
<constraint firstItem="B4o-Rc-z68" firstAttribute="top" secondItem="kEV-0h-NsX" secondAttribute="top" constant="221" id="rE0-BW-UQv"/>
<constraint firstItem="DV4-GV-ZPf" firstAttribute="top" secondItem="kEV-0h-NsX" secondAttribute="top" constant="460" id="tPa-CI-8ak"/>
</constraints>
</view>
<blurEffect style="dark"/>
</visualEffectView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8DU-2J-HAz" userLabel="Scanner Container">
<rect key="frame" x="0.0" y="-284" width="320" height="284"/>
<subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MhA-kC-nBb" userLabel="Scanner">
<rect key="frame" x="0.0" y="66" width="320" height="218"/>
<connections>
<segue destination="EFG-13-FgR" kind="embed" identifier="embedIdentityQRScanner" id="0sF-Is-2kw"/>
</connections>
</containerView>
</subviews>
<constraints>
<constraint firstItem="MhA-kC-nBb" firstAttribute="leading" secondItem="8DU-2J-HAz" secondAttribute="leading" id="EwE-x7-nyJ"/>
<constraint firstAttribute="trailing" secondItem="MhA-kC-nBb" secondAttribute="trailing" id="Hj7-wD-3wR"/>
<constraint firstItem="MhA-kC-nBb" firstAttribute="top" secondItem="8DU-2J-HAz" secondAttribute="top" constant="66" id="JTj-zm-L8f"/>
<constraint firstAttribute="bottom" secondItem="MhA-kC-nBb" secondAttribute="bottom" id="RuO-nQ-fOf"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="b7W-j4-3S1" userLabel="QR Container">
<rect key="frame" x="0.0" y="0.0" width="320" height="284"/>
<subviews>
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lA7-b4-o6C" userLabel="QR Frame">
<rect key="frame" x="54" y="66" width="212" height="212"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="btnQRShow--white" highlightedImage="btnQRShow--white-1" translatesAutoresizingMaskIntoConstraints="NO" id="YOs-e5-ee3" userLabel="Privacy Verification QR">
<rect key="frame" x="33" y="33" width="136" height="136"/>
<constraints>
<constraint firstAttribute="width" secondItem="YOs-e5-ee3" secondAttribute="height" multiplier="1:1" id="lN0-BE-w2c"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="lA7-b4-o6C" secondAttribute="height" multiplier="1:1" id="01p-Ui-d1b"/>
<constraint firstItem="YOs-e5-ee3" firstAttribute="centerY" secondItem="lA7-b4-o6C" secondAttribute="centerY" id="ZRp-fU-qZu"/>
<constraint firstItem="YOs-e5-ee3" firstAttribute="centerX" secondItem="lA7-b4-o6C" secondAttribute="centerX" id="oWJ-Z4-8ke"/>
<constraint firstItem="YOs-e5-ee3" firstAttribute="height" secondItem="lA7-b4-o6C" secondAttribute="height" multiplier="2/3" id="svC-b9-cy9"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="lA7-b4-o6C" firstAttribute="width" secondItem="b7W-j4-3S1" secondAttribute="width" multiplier="2/3" id="axL-M0-aaE"/>
<constraint firstItem="lA7-b4-o6C" firstAttribute="centerY" secondItem="b7W-j4-3S1" secondAttribute="centerY" constant="30" id="uHm-ee-HVb"/>
<constraint firstItem="lA7-b4-o6C" firstAttribute="centerX" secondItem="b7W-j4-3S1" secondAttribute="centerX" id="zzE-ab-Qv9"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="B4o-Rc-z68" userLabel="Instructions Container">
<rect key="frame" x="0.0" y="284" width="320" height="284"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="12345 54432 83456 89456 24327 87547 90123 31523 91052 84930 89304 00234" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e7E-iS-3Oc" userLabel="Privacy Verification Key Text">
<rect key="frame" x="36" y="0.0" width="248" height="70"/>
<constraints>
<constraint firstAttribute="height" constant="70" id="k9r-Fg-1QJ"/>
</constraints>
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="24"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scan the code on your contacts phone, or ask them to scan your code to verify that your messages are end-to-end encrypted." lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DV4-GV-ZPf" userLabel="Instructions ">
<rect key="frame" x="16" y="78" width="288" height="104"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44" id="bif-t1-qQO"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="top" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RX6-CJ-FV1">
<rect key="frame" x="90" y="202" width="141" height="70"/>
<constraints>
<constraint firstAttribute="height" constant="70" id="g0E-XL-o8K"/>
</constraints>
<inset key="titleEdgeInsets" minX="-50" minY="44" maxX="0.0" maxY="0.0"/>
<inset key="imageEdgeInsets" minX="43" minY="0.0" maxX="0.0" maxY="0.0"/>
<state key="normal" title="Scan Code" image="btnCamera--white">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="didTouchUpInsideScanButton:" destination="urv-62-RsD" eventType="touchUpInside" id="YJi-1a-Pg7"/>
</connections>
</button>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstItem="RX6-CJ-FV1" firstAttribute="centerX" secondItem="B4o-Rc-z68" secondAttribute="centerX" id="3Tz-CS-vgs"/>
<constraint firstItem="e7E-iS-3Oc" firstAttribute="leading" secondItem="B4o-Rc-z68" secondAttribute="leading" constant="36" id="7cQ-4a-DIS"/>
<constraint firstAttribute="bottom" secondItem="RX6-CJ-FV1" secondAttribute="bottom" constant="12" id="IFA-AS-nVw"/>
<constraint firstItem="e7E-iS-3Oc" firstAttribute="top" secondItem="B4o-Rc-z68" secondAttribute="top" id="NVF-Sc-fff"/>
<constraint firstItem="DV4-GV-ZPf" firstAttribute="top" secondItem="e7E-iS-3Oc" secondAttribute="bottom" constant="8" id="WaE-z7-y2j"/>
<constraint firstItem="RX6-CJ-FV1" firstAttribute="top" secondItem="DV4-GV-ZPf" secondAttribute="bottom" constant="20" id="XN2-Ok-U7y"/>
<constraint firstAttribute="trailing" secondItem="DV4-GV-ZPf" secondAttribute="trailing" constant="16" id="dFu-ak-X2j"/>
<constraint firstAttribute="trailing" secondItem="e7E-iS-3Oc" secondAttribute="trailing" constant="36" id="daW-7v-6MP"/>
<constraint firstItem="DV4-GV-ZPf" firstAttribute="leading" secondItem="B4o-Rc-z68" secondAttribute="leading" constant="16" id="sZ7-tO-0jJ"/>
</constraints>
<connections>
<outletCollection property="gestureRecognizers" destination="Mg3-ad-PGG" appends="YES" id="fDZ-ZY-J04"/>
</connections>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eyu-sj-AbV" userLabel="Top Bar">
<rect key="frame" x="0.0" y="22" width="320" height="44"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Yoz-ex-1gK">
<rect key="frame" x="0.0" y="0.0" width="44" height="44"/>
<constraints>
<constraint firstAttribute="width" constant="44" id="Ie5-wL-XEo"/>
<constraint firstAttribute="height" constant="44" id="aq2-hO-WY0"/>
</constraints>
<state key="normal" image="btnCancel--white"/>
<connections>
<action selector="closeButtonAction:" destination="urv-62-RsD" eventType="touchUpInside" id="m15-1K-9gB"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Safety Number for Momo" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vR4-Y9-ryx" userLabel="Safety Number for Momo">
<rect key="frame" x="40" y="0.0" width="240" height="45"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="Yoz-ex-1gK" firstAttribute="leading" secondItem="eyu-sj-AbV" secondAttribute="leading" id="3YT-t8-R98"/>
<constraint firstItem="vR4-Y9-ryx" firstAttribute="centerY" secondItem="eyu-sj-AbV" secondAttribute="centerY" id="9dS-Pk-hvb"/>
<constraint firstItem="Yoz-ex-1gK" firstAttribute="top" secondItem="eyu-sj-AbV" secondAttribute="top" id="Rfl-RX-bgS"/>
<constraint firstItem="vR4-Y9-ryx" firstAttribute="width" secondItem="eyu-sj-AbV" secondAttribute="width" multiplier="3/4" id="TaA-GT-RI3"/>
<constraint firstAttribute="height" constant="44" id="cMD-p0-7XC"/>
<constraint firstItem="vR4-Y9-ryx" firstAttribute="centerX" secondItem="eyu-sj-AbV" secondAttribute="centerX" id="oE0-Zr-7Kv"/>
<constraint firstItem="vR4-Y9-ryx" firstAttribute="top" secondItem="eyu-sj-AbV" secondAttribute="top" id="tED-yF-Auq"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="hdo-6G-gXy" firstAttribute="top" secondItem="VCu-vN-Pjg" secondAttribute="bottom" id="1dB-W2-GMz"/>
<constraint firstAttribute="centerX" secondItem="VCu-vN-Pjg" secondAttribute="centerX" id="5Wt-QM-aQa"/>
<constraint firstItem="VCu-vN-Pjg" firstAttribute="top" secondItem="8Oc-YZ-y0M" secondAttribute="bottom" constant="-20" id="KIN-pk-9pP"/>
<constraint firstAttribute="width" secondItem="VCu-vN-Pjg" secondAttribute="width" id="gOw-BF-mQC"/>
<constraint firstAttribute="height" secondItem="VCu-vN-Pjg" secondAttribute="height" id="nEQ-Wa-OyH"/>
<constraint firstAttribute="centerY" secondItem="VCu-vN-Pjg" secondAttribute="centerY" id="sho-vB-Yk8"/>
<constraint firstItem="8DU-2J-HAz" firstAttribute="leading" secondItem="StY-nr-mRe" secondAttribute="leading" id="00e-Rm-0Uk"/>
<constraint firstAttribute="trailing" secondItem="eyu-sj-AbV" secondAttribute="trailing" id="2mP-vt-cG0"/>
<constraint firstAttribute="trailing" secondItem="B4o-Rc-z68" secondAttribute="trailing" id="3ii-xe-El5"/>
<constraint firstItem="X9c-nY-Re0" firstAttribute="top" secondItem="StY-nr-mRe" secondAttribute="topMargin" id="4jD-8p-9y8"/>
<constraint firstItem="hdo-6G-gXy" firstAttribute="top" secondItem="B4o-Rc-z68" secondAttribute="bottom" id="ChJ-qg-hLJ"/>
<constraint firstItem="eyu-sj-AbV" firstAttribute="leading" secondItem="StY-nr-mRe" secondAttribute="leading" id="Dno-eX-doN"/>
<constraint firstItem="eyu-sj-AbV" firstAttribute="top" secondItem="StY-nr-mRe" secondAttribute="topMargin" constant="22" id="FwQ-7E-lv5"/>
<constraint firstItem="b7W-j4-3S1" firstAttribute="leading" secondItem="StY-nr-mRe" secondAttribute="leading" id="H4U-cb-hW7"/>
<constraint firstItem="X9c-nY-Re0" firstAttribute="leading" secondItem="StY-nr-mRe" secondAttribute="leading" id="JO9-ay-ebd"/>
<constraint firstItem="B4o-Rc-z68" firstAttribute="leading" secondItem="StY-nr-mRe" secondAttribute="leading" id="KhC-UZ-RCj"/>
<constraint firstAttribute="trailing" secondItem="b7W-j4-3S1" secondAttribute="trailing" id="PRX-ZC-wpQ"/>
<constraint firstItem="B4o-Rc-z68" firstAttribute="top" secondItem="b7W-j4-3S1" secondAttribute="bottom" id="V05-1p-r0f"/>
<constraint firstItem="b7W-j4-3S1" firstAttribute="top" secondItem="StY-nr-mRe" secondAttribute="topMargin" id="byY-vN-ywN"/>
<constraint firstItem="8DU-2J-HAz" firstAttribute="bottom" secondItem="b7W-j4-3S1" secondAttribute="top" id="fOH-xt-epL"/>
<constraint firstAttribute="trailing" secondItem="8DU-2J-HAz" secondAttribute="trailing" id="s7e-Xw-wGe"/>
<constraint firstItem="8DU-2J-HAz" firstAttribute="height" secondItem="StY-nr-mRe" secondAttribute="height" multiplier="1/2" id="sBp-BU-UlW"/>
<constraint firstItem="b7W-j4-3S1" firstAttribute="height" secondItem="StY-nr-mRe" secondAttribute="height" multiplier="1/2" id="wXF-0d-U9i"/>
<constraint firstAttribute="trailing" secondItem="X9c-nY-Re0" secondAttribute="trailing" id="xdd-Qu-mV8"/>
<constraint firstItem="hdo-6G-gXy" firstAttribute="top" secondItem="X9c-nY-Re0" secondAttribute="bottom" id="ySw-OZ-TGq"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="Gye-4f-x42"/>
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
<connections>
<outlet property="closeButton" destination="Yoz-ex-1gK" id="Z51-R1-bmj"/>
<outlet property="contactFingerprintLabel" destination="kBW-ix-mdQ" id="RN4-U7-Cpm"/>
<outlet property="contactFingerprintTitleLabel" destination="Ii9-7N-hCc" id="adg-hK-fTW"/>
<outlet property="contactImageView" destination="HyV-ht-MrM" id="eg1-Cw-B8S"/>
<outlet property="infoMyFingerprint" destination="9Wg-UP-VDC" id="78r-JU-b4l"/>
<outlet property="infoTheirFingerprint" destination="ge0-8K-A4F" id="DyU-To-aJG"/>
<outlet property="myFPBorderView" destination="WhZ-e0-THb" id="SrI-AN-3OJ"/>
<outlet property="myFingerprintView" destination="B4o-Rc-z68" id="FjV-Xs-aV8"/>
<outlet property="presentationLabel" destination="DV4-GV-ZPf" id="pz1-G0-dVx"/>
<outlet property="theirFingerprintView" destination="zFI-eF-Feb" id="UqY-Fb-DLM"/>
<outlet property="userFingerprintLabel" destination="e7E-iS-3Oc" id="aR7-ww-NCi"/>
<outlet property="userFingerprintTitleLabel" destination="5De-Jq-XWv" id="68F-0n-IMn"/>
<outlet property="userImageView" destination="YOs-e5-ee3" id="zC3-Wp-ew3"/>
<segue destination="7qX-e8-QDY" kind="modal" identifier="ScanIdentityBarcodeViewSegue" modalTransitionStyle="crossDissolve" id="aSp-qz-VlO"/>
<segue destination="in8-UP-6ff" kind="modal" identifier="PresentIdentityQRCodeViewSegue" modalTransitionStyle="crossDissolve" id="mQB-3L-0HK"/>
<outlet property="instructionsContainer" destination="B4o-Rc-z68" id="Rc4-wf-8jw"/>
<outlet property="instructionsLabel" destination="DV4-GV-ZPf" id="f1p-P7-QOi"/>
<outlet property="privacyVerificationFingerprint" destination="e7E-iS-3Oc" id="IA0-ZU-vKJ"/>
<outlet property="privacyVerificationQRCode" destination="YOs-e5-ee3" id="Sbg-HQ-tnD"/>
<outlet property="privacyVerificationQRCodeFrame" destination="lA7-b4-o6C" id="ngQ-6e-F2W"/>
<outlet property="qrCodeCenterConstraint" destination="uHm-ee-HVb" id="8DE-qh-Qif"/>
<outlet property="qrContainer" destination="b7W-j4-3S1" id="IpY-w1-yk6"/>
<outlet property="qrScanningView" destination="MhA-kC-nBb" id="gNX-7s-wIG"/>
<outlet property="scanButton" destination="RX6-CJ-FV1" id="BKY-4g-g3U"/>
<outlet property="scanningContainer" destination="8DU-2J-HAz" id="FH9-yo-8uI"/>
<outlet property="titleLabel" destination="vR4-Y9-ryx" id="l8Y-0N-eKa"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="sd1-mY-rAe" userLabel="First Responder" sceneMemberID="firstResponder"/>
<tapGestureRecognizer id="fRl-Lt-y39">
<connections>
<action selector="scanFingerprint" destination="urv-62-RsD" id="e0q-jy-keh"/>
</connections>
</tapGestureRecognizer>
<tapGestureRecognizer id="Mg3-ad-PGG">
<connections>
<action selector="showFingerprint" destination="urv-62-RsD" id="wcQ-Di-UbR"/>
</connections>
</tapGestureRecognizer>
<tapGestureRecognizer id="fRl-Lt-y39"/>
<tapGestureRecognizer id="Mg3-ad-PGG"/>
</objects>
<point key="canvasLocation" x="-1944" y="-2339"/>
</scene>
@ -381,7 +329,7 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="22" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hyn-Ss-OAa" id="4XE-JO-Upr">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
@ -397,107 +345,6 @@ A0 09 9A FF A8 8A 09 99</string>
</objects>
<point key="canvasLocation" x="-1935.2" y="-1537.8599999999999"/>
</scene>
<!--Present IdentityQR Code View Controller-->
<scene sceneID="b0A-fL-uiD">
<objects>
<viewController storyboardIdentifier="PresentIdentityQRCodeViewController" id="in8-UP-6ff" customClass="PresentIdentityQRCodeViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="EEv-2N-b9z"/>
<viewControllerLayoutGuide type="bottom" id="PWz-Os-GfE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8fX-rK-XSk">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FVU-rR-RPG">
<rect key="frame" x="16" y="510" width="50" height="50"/>
<state key="normal" image="quit.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<segue destination="MWg-NL-WXh" kind="unwind" unwindAction="unwindIdentityVerificationCancel:" id="zG6-lN-44J"/>
</connections>
</button>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="I4l-hR-EdB">
<rect key="frame" x="35" y="159" width="250" height="250"/>
<constraints>
<constraint firstAttribute="height" constant="250" id="WS4-Wb-iXP"/>
<constraint firstAttribute="width" constant="250" id="n5C-hu-0F6"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your Fingerprint" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Psg-eI-6bn">
<rect key="frame" x="50" y="130" width="220" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="220" id="IoY-EK-Qhk"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="FVU-rR-RPG" firstAttribute="leading" secondItem="8fX-rK-XSk" secondAttribute="leadingMargin" id="4fp-jK-Pgt"/>
<constraint firstAttribute="centerX" secondItem="I4l-hR-EdB" secondAttribute="centerX" id="He1-BL-c7O"/>
<constraint firstAttribute="centerY" secondItem="I4l-hR-EdB" secondAttribute="centerY" id="dyA-NM-Zc5"/>
<constraint firstItem="Psg-eI-6bn" firstAttribute="centerX" secondItem="8fX-rK-XSk" secondAttribute="centerX" id="gFt-YV-WTg"/>
<constraint firstItem="PWz-Os-GfE" firstAttribute="top" secondItem="FVU-rR-RPG" secondAttribute="bottom" constant="8" symbolic="YES" id="k4n-8r-ocu"/>
<constraint firstItem="I4l-hR-EdB" firstAttribute="top" secondItem="Psg-eI-6bn" secondAttribute="bottom" constant="8.5" id="nyu-mu-lcL"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="zty-sG-NJJ"/>
<connections>
<outlet property="qrCodeView" destination="I4l-hR-EdB" id="Fzf-BD-z9y"/>
<outlet property="yourFingerprintLabel" destination="Psg-eI-6bn" id="1Ze-k1-zMv"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Zk1-cK-ftT" userLabel="First Responder" sceneMemberID="firstResponder"/>
<exit id="MWg-NL-WXh" userLabel="Exit" sceneMemberID="exit"/>
</objects>
<point key="canvasLocation" x="-1495" y="-1940"/>
</scene>
<!--Scan Identity Barcode View Controller-->
<scene sceneID="f84-pF-1is">
<objects>
<viewController storyboardIdentifier="ScanIdentityBarcodeViewController" id="7qX-e8-QDY" customClass="ScanIdentityBarcodeViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="VoP-Af-AZ2"/>
<viewControllerLayoutGuide type="bottom" id="vey-9m-z4V"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Du7-nD-ScY">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bag-cN-IZy">
<rect key="frame" x="16" y="498" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="Odb-rK-4Va"/>
<constraint firstAttribute="height" constant="50" id="ZPA-Mg-Y3j"/>
</constraints>
<state key="normal" image="quit.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<segue destination="aQ6-9V-CJA" kind="unwind" unwindAction="unwindIdentityVerificationCancel:" id="bx5-cq-tqn"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="bag-cN-IZy" firstAttribute="leading" secondItem="Du7-nD-ScY" secondAttribute="leadingMargin" id="vL9-x7-knG"/>
<constraint firstAttribute="bottom" secondItem="bag-cN-IZy" secondAttribute="bottom" constant="20" symbolic="YES" id="zaD-R1-jw2"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="I7H-1E-cUg"/>
<connections>
<segue destination="aQ6-9V-CJA" kind="unwind" identifier="UnwindToIdentityKeyWasVerifiedSegue" unwindAction="unwindToIdentityKeyWasVerified:" id="Eyg-Zl-I3n"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="VTv-Ry-X99" userLabel="First Responder" sceneMemberID="firstResponder"/>
<exit id="aQ6-9V-CJA" userLabel="Exit" sceneMemberID="exit"/>
</objects>
<point key="canvasLocation" x="-1495" y="-2584"/>
</scene>
<!--_1.0 Registration Screen-->
<scene sceneID="okO-46-HuB">
<objects>
@ -746,7 +593,7 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="130" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hpG-pz-GPZ" id="gVo-Nw-ff7" userLabel="Country Code Table Row">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="United states" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SDz-Ag-lp3">
@ -1361,10 +1208,10 @@ A0 09 9A FF A8 8A 09 99</string>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="ExistingDevice" rowHeight="72" id="XjV-oU-jSb" customClass="OWSDeviceTableViewCell">
<rect key="frame" x="0.0" y="113.5" width="320" height="72"/>
<rect key="frame" x="0.0" y="114" width="320" height="72"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="XjV-oU-jSb" id="XqL-QG-IbY">
<rect key="frame" x="0.0" y="0.0" width="320" height="71.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="71"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Signal on Chrome" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="57o-uV-YOg">
@ -1373,13 +1220,13 @@ A0 09 9A FF A8 8A 09 99</string>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Linked: Jun 12, 2016" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uL2-wj-OZr">
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" misplaced="YES" text="Linked: Jun 12, 2016" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uL2-wj-OZr">
<rect key="frame" x="17" y="28" width="116" height="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Last Seen: Aug 25, 2016" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kek-MK-oLy">
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" misplaced="YES" text="Last Seen: Aug 25, 2016" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kek-MK-oLy">
<rect key="frame" x="17" y="45" width="138" height="18"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@ -1405,21 +1252,21 @@ A0 09 9A FF A8 8A 09 99</string>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="AddNewDevice" textLabel="w80-IJ-E6R" detailTextLabel="8ft-2u-wBF" style="IBUITableViewCellStyleSubtitle" id="6h2-gg-1C6">
<rect key="frame" x="0.0" y="185.5" width="320" height="44"/>
<rect key="frame" x="0.0" y="186" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="6h2-gg-1C6" id="RKi-c6-pzb">
<rect key="frame" x="0.0" y="0.0" width="287" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="287" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Link New Device" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="w80-IJ-E6R">
<rect key="frame" x="15" y="5" width="120.5" height="19.5"/>
<rect key="frame" x="15" y="4" width="121" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Scan QR Code" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8ft-2u-wBF">
<rect key="frame" x="15" y="24.5" width="82" height="14.5"/>
<rect key="frame" x="15" y="24" width="82" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@ -1458,7 +1305,7 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="64" width="320" height="118"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="5zF-Ko-9qU" id="gr7-Sm-bcs">
<rect key="frame" x="0.0" y="0.0" width="320" height="117.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="117"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1 (708) 000-1234" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ipE-BI-sLL">
@ -1499,7 +1346,7 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="182" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8rk-06-1ZS" id="hqv-P5-du9">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Network Status" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uNq-FV-lwt">
@ -1539,11 +1386,11 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="226" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ITG-sW-Zn0" id="vOb-SA-SH2">
<rect key="frame" x="0.0" y="0.0" width="287" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="287" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Privacy" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="i1f-DT-7rL">
<rect key="frame" x="15" y="0.0" width="270" height="43.5"/>
<rect key="frame" x="15" y="0.0" width="270" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@ -1556,11 +1403,11 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="270" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jp5-vZ-AhJ" id="sji-CJ-bhq">
<rect key="frame" x="0.0" y="0.0" width="287" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="287" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Notifications" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tRQ-1p-6aT">
<rect key="frame" x="15" y="0.0" width="270" height="43.5"/>
<rect key="frame" x="15" y="0.0" width="270" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@ -1573,11 +1420,11 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="314" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="wZ8-fs-Ylw" id="Ua5-nw-s2z">
<rect key="frame" x="0.0" y="0.0" width="287" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="287" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Linked Devices" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hrc-lZ-NeA">
<rect key="frame" x="15" y="0.0" width="270" height="43.5"/>
<rect key="frame" x="15" y="0.0" width="270" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@ -1593,11 +1440,11 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="358" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Xx7-pz-aLN" id="pMA-vR-8Ae">
<rect key="frame" x="0.0" y="0.0" width="287" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="287" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Advanced" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dkL-Nz-E6H">
<rect key="frame" x="15" y="0.0" width="270" height="43.5"/>
<rect key="frame" x="15" y="0.0" width="270" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@ -1610,11 +1457,11 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="402" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="EI4-kQ-MMA" id="czg-5p-aVz">
<rect key="frame" x="0.0" y="0.0" width="287" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="287" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="About" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="qeN-f1-cIQ">
<rect key="frame" x="15" y="0.0" width="270" height="43.5"/>
<rect key="frame" x="15" y="0.0" width="270" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@ -1631,7 +1478,7 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="446" width="320" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="R82-FT-SEA" id="Ok9-fE-WhB">
<rect key="frame" x="0.0" y="0.0" width="320" height="99.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="99"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4Mk-ly-6fq">
@ -1729,7 +1576,7 @@ A0 09 9A FF A8 8A 09 99</string>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scan the QR code displayed on the device to link." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="3D8-yn-TJ8">
<rect key="frame" x="0.0" y="161" width="288" height="20"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
@ -1815,7 +1662,7 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="22" width="320" height="48"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Ld5-sX-pB8" id="EqP-87-4hZ">
<rect key="frame" x="0.0" y="0.0" width="320" height="47.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="47"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="urb-Me-knG">
@ -1938,11 +1785,11 @@ A0 09 9A FF A8 8A 09 99</string>
<rect key="frame" x="0.0" y="62" width="320" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="yfF-Jl-bZ1" id="f0v-od-N9K">
<rect key="frame" x="0.0" y="0.0" width="320" height="59.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="59"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="a4j-OQ-ala">
<rect key="frame" x="15" y="0.0" width="290" height="59.5"/>
<rect key="frame" x="15" y="0.0" width="290" height="59"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="20"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
@ -2009,10 +1856,10 @@ A0 09 9A FF A8 8A 09 99</string>
</objects>
<point key="canvasLocation" x="-4372" y="-732.72000000000003"/>
</scene>
<!--Code Scanning View Controller-->
<!--Device QR Scanner-->
<scene sceneID="f3S-St-vF2">
<objects>
<viewController id="xDh-Mk-Yo9" customClass="OWSQRCodeScanningViewController" sceneMemberID="viewController">
<viewController title="Device QR Scanner" id="xDh-Mk-Yo9" customClass="OWSQRCodeScanningViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="YV9-ra-E3e"/>
<viewControllerLayoutGuide type="bottom" id="s4y-gf-WU5"/>
@ -2027,6 +1874,24 @@ A0 09 9A FF A8 8A 09 99</string>
</objects>
<point key="canvasLocation" x="-1632" y="-3228"/>
</scene>
<!--Identity QR Scanner-->
<scene sceneID="BBm-xb-dFO">
<objects>
<viewController title="Identity QR Scanner" id="EFG-13-FgR" customClass="OWSQRCodeScanningViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="xM5-Dm-A1I"/>
<viewControllerLayoutGuide type="bottom" id="w3c-y6-c5W"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Z7D-4g-GIf">
<rect key="frame" x="0.0" y="0.0" width="320" height="218"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ExB-N2-kxx" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1516" y="-2450"/>
</scene>
</scenes>
<resources>
<image name="_arrow_button" width="12" height="23"/>
@ -2043,7 +1908,6 @@ A0 09 9A FF A8 8A 09 99</string>
<image name="logoSignal" width="138" height="139"/>
<image name="mute-active" width="80" height="80"/>
<image name="mute-inactive" width="80" height="80"/>
<image name="quit.png" width="50" height="50"/>
<image name="settings" width="44" height="44"/>
<image name="speaker-active" width="80" height="80"/>
<image name="speaker-inactive" width="80" height="80"/>

View File

@ -398,6 +398,10 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
}
- (NSString *)nameStringForPhoneIdentifier:(NSString *)identifier {
if (!identifier) {
return NSLocalizedString(@"UNKNOWN_CONTACT_NAME",
@"Displayed if for some reason we can't determine a contacts phone number *or* name");
}
for (Contact *contact in self.allContacts) {
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
if ([phoneNumber.toE164 isEqualToString:identifier]) {
@ -405,7 +409,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
}
}
}
return nil;
return identifier;
}
- (UIImage *)imageForPhoneIdentifier:(NSString *)identifier {

View File

@ -32,7 +32,6 @@
#define TXT_CANCEL_TITLE NSLocalizedString(@"TXT_CANCEL_TITLE", @"")
#define TXT_SEARCH_PLACEHOLDER_TEXT NSLocalizedString(@"TXT_SEARCH_PLACEHOLDER_TEXT", @"")
#define UNKNOWN_CONTACT_NAME NSLocalizedString(@"UNKNOWN_CONTACT_NAME", @"")
#pragma mark - Inbox View

View File

@ -20,6 +20,7 @@
@interface NotificationsManager ()
@property SystemSoundID newMessageSound;
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@end
@ -33,6 +34,8 @@
return self;
}
_contactsManager = [TextSecureKitEnv sharedEnv].contactsManager;
NSURL *newMessageURL = [[NSBundle mainBundle] URLForResource:@"NewMessage" withExtension:@"aifc"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)newMessageURL, &_newMessageSound);
@ -110,12 +113,7 @@
@{Signal_Thread_UserInfo_Key : thread.uniqueId, Signal_Message_UserInfo_Key : message.uniqueId};
if ([thread isGroupThread]) {
NSString *sender =
[[TextSecureKitEnv sharedEnv].contactsManager nameStringForPhoneIdentifier:message.authorId];
if (!sender) {
sender = message.authorId;
}
NSString *sender = [self.contactsManager nameStringForPhoneIdentifier:message.authorId];
NSString *threadName = [NSString stringWithFormat:@"\"%@\"", name];
notification.alertBody =
[NSString stringWithFormat:NSLocalizedString(@"APN_MESSAGE_IN_GROUP_DETAILED", nil),

View File

@ -28,6 +28,7 @@
@property UILocalNotification *lastCallNotification;
@property (nonatomic, retain) NSMutableArray *currentNotifications;
@property (nonatomic) UIBackgroundTaskIdentifier callBackgroundTask;
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@end
@ -44,17 +45,20 @@
- (instancetype)init {
self = [super init];
if (self) {
self.notificationTracker = [NotificationTracker notificationTracker];
self.missingPermissionsAlertView =
[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"")
message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"")
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", @"")
otherButtonTitles:nil, nil];
_callBackgroundTask = UIBackgroundTaskInvalid;
self.currentNotifications = [NSMutableArray array];
if (!self) {
return self;
}
_contactsManager = [Environment getCurrent].contactsManager;
_notificationTracker = [NotificationTracker notificationTracker];
_missingPermissionsAlertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"")
message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"")
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", @"")
otherButtonTitles:nil, nil];
_callBackgroundTask = UIBackgroundTaskInvalid;
_currentNotifications = [NSMutableArray array];
return self;
}
@ -86,9 +90,7 @@
UILocalNotification *notification = [[UILocalNotification alloc] init];
NSString *callerId = call.initiatorNumber.toE164;
NSString *nameString = [[Environment getCurrent].contactsManager nameStringForPhoneIdentifier:callerId];
NSString *displayName = nameString ? nameString : callerId;
NSString *displayName = [self.contactsManager nameStringForPhoneIdentifier:callerId];
PropertyListPreferences *prefs = [Environment preferences];
notification.alertBody = @"☎️ ";
@ -211,7 +213,7 @@
} else if ([identifier isEqualToString:Signal_CallBack_Identifier]) {
NSString *contactId = notification.userInfo[Signal_Call_UserInfo_Key];
PhoneNumber *number = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:contactId];
Contact *contact = [[Environment.getCurrent contactsManager] latestContactForPhoneNumber:number];
Contact *contact = [self.contactsManager latestContactForPhoneNumber:number];
[Environment.phoneManager initiateOutgoingCallToContact:contact atRemoteNumber:number];
} else if ([identifier isEqualToString:Signal_Message_MarkAsRead_Identifier]) {
[self markAllInThreadAsRead:notification.userInfo completionHandler:completionHandler];

View File

@ -6,46 +6,17 @@
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "MessagesViewController.h"
#import "TSContactThread.h"
#import "OWSQRCodeScanningViewController.h"
@interface FingerprintViewController : UIViewController
NS_ASSUME_NONNULL_BEGIN
- (void)configWithThread:(TSThread *)thread;
@class OWSFingerprint;
@property (nonatomic, strong) IBOutlet UILabel *presentationLabel;
@interface FingerprintViewController : UIViewController <OWSQRScannerDelegate>
@property (nonatomic, strong) IBOutlet UIView *myFingerprintView;
@property (nonatomic, strong) IBOutlet UIView *theirFingerprintView;
- (void)configureWithFingerprint:(OWSFingerprint *)fingerprint contactName:(NSString *)contactName;
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data;
@property (nonatomic, strong) IBOutlet UIImageView *contactImageView;
@property (nonatomic, strong) IBOutlet UILabel *contactFingerprintTitleLabel;
@property (nonatomic, strong) IBOutlet UILabel *contactFingerprintLabel;
@property (nonatomic, strong) IBOutlet UIImageView *userImageView;
@property (nonatomic, strong) IBOutlet UILabel *userFingerprintTitleLabel;
@property (nonatomic, strong) IBOutlet UILabel *userFingerprintLabel;
@property (nonatomic, strong) IBOutlet UILabel *infoMyFingerprint;
@property (nonatomic, strong) IBOutlet UILabel *infoTheirFingerprint;
@property (nonatomic, strong) IBOutlet UIImageView *infoArrowTop;
@property (nonatomic, strong) IBOutlet UIImageView *infoArrowBottom;
@property (nonatomic, strong) IBOutlet UIButton *closeButton;
@property (nonatomic, strong) IBOutlet UIView *myFPBorderView;
// returns my public identity key as NSData
- (NSData *)getMyPublicIdentityKey;
// returns recipient's public identity key as NSData
- (NSData *)getTheirPublicIdentityKey;
// This is called when the recipient's public key is verified. Later can be used to mark as such if we want a step above
// TOFU in UX.
- (IBAction)unwindToIdentityKeyWasVerified:(UIStoryboardSegue *)segue;
// Just a cancelation of the user's request to verify reciepient fingerprint or have their fingerprint verified by
// recipient
- (IBAction)unwindIdentityVerificationCancel:(UIStoryboardSegue *)segue;
@end
NS_ASSUME_NONNULL_END

View File

@ -7,102 +7,112 @@
//
#import "FingerprintViewController.h"
#import <25519/Curve25519.h>
#import <AxolotlKit/NSData+keyVersionByte.h>
#import "DJWActionSheet+OWS.h"
#import "PresentIdentityQRCodeViewController.h"
#import "ScanIdentityBarcodeViewController.h"
#import "SignalsNavigationController.h"
#import <SignalServiceKit/OWSFingerprint.h>
#import <SignalServiceKit/TSStorageManager+IdentityKeyStore.h>
#import <SignalServiceKit/TSStorageManager+SessionStore.h>
#import <SignalServiceKit/TSStorageManager+keyingMaterial.h>
#import "TSFingerprintGenerator.h"
#import "TSStorageHeaders.h"
NS_ASSUME_NONNULL_BEGIN
@interface FingerprintViewController ()
@property TSContactThread *thread;
@property (nonatomic) BOOL isPresentingDialog;
@end
static NSString *const kPresentIdentityQRCodeViewSegue = @"PresentIdentityQRCodeViewSegue";
static NSString *const kScanIdentityBarcodeViewSegue = @"ScanIdentityBarcodeViewSegue";
@property (strong, nonatomic) TSStorageManager *storageManager;
@property (nonatomic) BOOL isPresentingDialog;
@property (strong, atomic) OWSFingerprint *fingerprint;
@property (strong, atomic) NSString *contactName;
@property (strong, nonatomic) OWSQRCodeScanningViewController *qrScanningController;
@property (strong, nonatomic) IBOutlet UIView *qrScanningView;
@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 UILabel *privacyVerificationFingerprint;
@property (strong, nonatomic) IBOutlet UILabel *instructionsLabel;
@property (strong, nonatomic) IBOutlet UILabel *titleLabel;
@property (strong, nonatomic) IBOutlet UIButton *scanButton;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *qrCodeCenterConstraint;
@end
@implementation FingerprintViewController
- (void)configWithThread:(TSThread *)thread {
self.thread = (TSContactThread *)thread;
- (void)configureWithFingerprint:(OWSFingerprint *)fingerprint contactName:(NSString *)contactName
{
self.fingerprint = fingerprint;
self.contactName = contactName;
}
- (void)viewDidLoad {
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setAlpha:0];
self.storageManager = [TSStorageManager sharedManager];
self.qrScanningView.hidden = YES;
// HACK to get full width preview layer
CGRect oldFrame = self.qrScanningView.frame;
CGRect newFrame = CGRectMake(oldFrame.origin.x,
oldFrame.origin.y,
self.view.frame.size.width,
self.view.frame.size.height / 2.0f - oldFrame.origin.y);
self.qrScanningView.frame = newFrame;
// END HACK to get full width preview layer
self.titleLabel.text = NSLocalizedString(@"PRIVACY_VERIFICATION_TITLE", @"Navbar title");
NSString *instructionsFormat = NSLocalizedString(@"PRIVACY_VERIFICATION_INSTRUCTIONS",
@"Paragraph(s) shown alongside keying material when verifying privacy with {{contact name}}");
self.instructionsLabel.text = [NSString stringWithFormat:instructionsFormat, self.contactName];
self.scanButton.titleLabel.text = NSLocalizedString(@"SCAN_CODE_ACTION",
@"Button label presented with camera icon while verifying privacy credentials. Shows the camera interface.");
// Safety numbers and QR Code
self.privacyVerificationFingerprint.text = self.fingerprint.displayableText;
self.privacyVerificationQRCode.image = self.fingerprint.image;
// Don't antialias QRCode
self.privacyVerificationQRCode.layer.magnificationFilter = kCAFilterNearest;
// Add session reset action.
UILongPressGestureRecognizer *longpressToResetSession =
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(shredAndDelete:)];
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongpressToResetSession:)];
longpressToResetSession.minimumPressDuration = 1.0;
[self.view addGestureRecognizer:longpressToResetSession];
_infoTheirFingerprint.text = NSLocalizedString(@"FINGERPRINT_INFO_THEIRS", @"");
_infoMyFingerprint.text = NSLocalizedString(@"FINGERPRINT_INFO_YOURS", @"");
_presentationLabel.text = NSLocalizedString(@"FINGERPRINT_INFO_ABOUT", @"");
_userFingerprintTitleLabel.text = NSLocalizedString(@"FINGERPRINT_YOURS", @"");
}
if ([UIScreen mainScreen].bounds.size.height <= 480) {
self.presentationLabel.hidden = YES;
self.myFPBorderView.hidden = YES;
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.privacyVerificationQRCodeFrame.layer.masksToBounds = YES;
self.privacyVerificationQRCodeFrame.layer.cornerRadius = self.privacyVerificationQRCodeFrame.frame.size.height / 2;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender
{
if ([segue.identifier isEqualToString:@"embedIdentityQRScanner"]) {
OWSQRCodeScanningViewController *qrScanningController
= (OWSQRCodeScanningViewController *)segue.destinationViewController;
self.qrScanningController = qrScanningController;
qrScanningController.scanDelegate = self;
}
}
- (void)viewWillAppear:(BOOL)animated {
[self setTheirKeyInformation];
NSData *myPublicKey = [[TSStorageManager sharedManager] identityKeyPair].publicKey;
self.userFingerprintLabel.text = [TSFingerprintGenerator getFingerprintForDisplay:myPublicKey];
[UIView animateWithDuration:0.6
delay:0.
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
[self.view setAlpha:1];
}
completion:nil];
}
- (void)setTheirKeyInformation {
self.contactFingerprintTitleLabel.text = self.thread.name;
NSData *identityKey = [[TSStorageManager sharedManager] identityKeyForRecipientId:self.thread.contactIdentifier];
self.contactFingerprintLabel.text = [TSFingerprintGenerator getFingerprintForDisplay:identityKey];
if ([self.contactFingerprintLabel.text length] == 0) {
// no fingerprint, hide this view
_presentationLabel.hidden = YES;
_theirFingerprintView.hidden = YES;
}
}
- (NSData *)getMyPublicIdentityKey {
return [[TSStorageManager sharedManager] identityKeyPair].publicKey;
}
- (NSData *)getTheirPublicIdentityKey {
return [[TSStorageManager sharedManager] identityKeyForRecipientId:self.thread.contactIdentifier];
}
#pragma mark - Action
- (IBAction)closeButtonAction:(id)sender {
[UIView animateWithDuration:0.6
delay:0.
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
[self.view setAlpha:0];
}
completion:^(BOOL succeeded) {
[self dismissViewControllerAnimated:NO completion:nil];
}];
- (IBAction)closeButtonAction:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)didTouchUpInsideScanButton:(id)sender
{
[self showScanner];
}
- (IBAction)shredAndDelete:(id)sender {
- (IBAction)didLongpressToResetSession:(id)sender
{
if (!_isPresentingDialog) {
_isPresentingDialog = YES;
[DJWActionSheet showInView:self.view
@ -119,7 +129,7 @@ static NSString *const kScanIdentityBarcodeViewSegue = @"ScanIdentityBarcodeVi
} else {
switch (tappedButtonIndex) {
case 0:
[self shredKeyingMaterial];
[self resetSession];
break;
default:
break;
@ -129,57 +139,117 @@ static NSString *const kScanIdentityBarcodeViewSegue = @"ScanIdentityBarcodeVi
}
}
- (void)showScanner
{
DDLogInfo(@"%@ Showing Scanner", self.tag);
self.qrScanningView.hidden = NO;
- (IBAction)showFingerprint {
[self performSegueWithIdentifier:kPresentIdentityQRCodeViewSegue sender:self];
// Recommended before animating a constraint.
[self.view layoutIfNeeded];
// Shift QRCode up within it's own frame, while shifting it's whole
// frame down.
self.qrCodeCenterConstraint.constant = 0.0f;
[UIView animateWithDuration:0.4
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.scanningContainer.frame = self.qrContainer.frame;
self.qrContainer.frame = self.instructionsContainer.frame;
self.instructionsContainer.alpha = 0.0f;
// animate constraint smoothly
[self.view layoutIfNeeded];
}
completion:nil];
[self.qrScanningController startCapture];
}
- (IBAction)scanFingerprint {
[self performSegueWithIdentifier:kScanIdentityBarcodeViewSegue sender:self];
- (void)resetSession
{
[self.storageManager removeIdentityKeyForRecipient:self.fingerprint.theirStableId];
[self.storageManager deleteAllSessionsForContact:self.fingerprint.theirStableId];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:kPresentIdentityQRCodeViewSegue]) {
[segue.destinationViewController setIdentityKey:[[self getMyPublicIdentityKey] prependKeyType]];
} else if ([[segue identifier] isEqualToString:kScanIdentityBarcodeViewSegue]) {
[segue.destinationViewController setIdentityKey:[[self getTheirPublicIdentityKey] prependKeyType]];
// pragma mark - OWSQRScannerDelegate
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data;
{
[self verifyCombinedFingerprintData:data];
}
- (void)verifyCombinedFingerprintData:(NSData *)combinedFingerprintData
{
NSError *error;
if ([self.fingerprint matchesCombinedFingerprintData:combinedFingerprintData error:&error]) {
DDLogInfo(@"%@ Successfully verified privacy.", self.tag);
NSString *successTitle = NSLocalizedString(@"SUCCESSFUL_VERIFICATION_TITLE", nil);
NSString *dismissText = NSLocalizedString(@"DISMISS_BUTTON_TEXT", nil);
NSString *descriptionFormat = NSLocalizedString(
@"SUCCESSFUL_VERIFICATION_DESCRIPTION", @"Alert body after verifying privacy with {{other user's name}}");
NSString *successDescription = [NSString stringWithFormat:descriptionFormat, self.contactName];
UIAlertController *successAlertController =
[UIAlertController alertControllerWithTitle:successTitle
message:successDescription
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *dismissAction =
[UIAlertAction actionWithTitle:dismissText
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[self dismissViewControllerAnimated:YES completion:nil];
}];
[successAlertController addAction:dismissAction];
[self presentViewController:successAlertController animated:YES completion:nil];
} else {
[self failVerificationWithError:error];
}
}
- (void)failVerificationWithError:(NSError *)error
{
NSString *failureTitle = NSLocalizedString(@"FAILED_VERIFICATION_TITLE", @"alert title");
UIAlertController *failureAlertController =
[UIAlertController alertControllerWithTitle:failureTitle
message:error.localizedDescription
preferredStyle:UIAlertControllerStyleAlert];
- (IBAction)unwindToIdentityKeyWasVerified:(UIStoryboardSegue *)segue {
// Can later be used to mark identity key as verified if we want step above TOFU in UX
NSString *cancelText = NSLocalizedString(@"TXT_CANCEL_TITLE", nil);
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelText
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *_Nonnull action) {
[self dismissViewControllerAnimated:YES completion:nil];
}];
[failureAlertController addAction:cancelAction];
// TODO
// NSString retryText = NSLocalizedString(@"RETRY_BUTTON_TEXT", nil);
// UIAlertAction *retryAction = [UIAlertAction actionWithTitle:retryText style:UIAlertActionStyleDefault
// handler:^(UIAlertAction * _Nonnull action) {
//
// }];
// [failureAlertController addAction:retryAction];
[self presentViewController:failureAlertController animated:YES completion:nil];
DDLogWarn(@"%@ Identity verification failed with error: %@", self.tag, error);
}
- (IBAction)unwindIdentityVerificationCancel:(UIStoryboardSegue *)segue {
DDLogDebug(@"action cancelled");
// Can later be used to mark identity key as verified if we want step above TOFU in UX
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(nullable void (^)(void))completion
{
self.qrScanningView.hidden = YES;
[super dismissViewControllerAnimated:flag completion:completion];
}
#pragma mark - Shredding & Deleting
- (void)shredKeyingMaterial {
[[TSStorageManager sharedManager] removeIdentityKeyForRecipient:self.thread.contactIdentifier];
[[TSStorageManager sharedManager] deleteAllSessionsForContact:self.thread.contactIdentifier];
[self setTheirKeyInformation];
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (void)shredDiscussionsWithContact {
UINavigationController *nVC = (UINavigationController *)self.presentingViewController;
for (UIViewController __strong *vc in nVC.viewControllers) {
if ([vc isKindOfClass:[MessagesViewController class]]) {
vc = nil;
}
}
[self.thread remove]; // this removes the thread and all it's discussion (YapDatabaseRelationships)
__block SignalsNavigationController *vc = (SignalsNavigationController *)[self presentingViewController];
[vc dismissViewControllerAnimated:YES
completion:^{
[vc popToRootViewControllerAnimated:YES];
}];
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -28,6 +28,7 @@
#import "SignalKeyingStorage.h"
#import "TSAttachmentPointer.h"
#import "TSCall.h"
#import "TSContactThread.h"
#import "TSContentAdapters.h"
#import "TSDatabaseView.h"
#import "TSErrorMessage.h"
@ -49,6 +50,7 @@
#import <JSQSystemSoundPlayer.h>
#import <MobileCoreServices/UTCoreTypes.h>
#import <SignalServiceKit/MimeTypeUtil.h>
#import <SignalServiceKit/OWSFingerprint.h>
#import <SignalServiceKit/SignalRecipient.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <YapDatabase/YapDatabaseView.h>
@ -98,14 +100,16 @@ typedef enum : NSUInteger {
@property (nonatomic, retain) UIButton *attachButton;
@property (nonatomic, retain) NSIndexPath *lastDeliveredMessageIndexPath;
@property (nonatomic, retain) UIGestureRecognizer *showFingerprintDisplay;
@property (nonatomic, retain) UITapGestureRecognizer *toggleContactPhoneDisplay;
@property (nonatomic, retain) UIGestureRecognizer *showFingerprintGesture;
@property (nonatomic, retain) UITapGestureRecognizer *toggleContactPhoneGesture;
@property (nonatomic) BOOL displayPhoneAsTitle;
@property NSUInteger page;
@property (nonatomic) BOOL composeOnOpen;
@property (nonatomic) BOOL peek;
@property (nonatomic, readonly) TSStorageManager *storageManager;
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@property NSCache *messageAdapterCache;
@end
@ -122,6 +126,32 @@ typedef enum : NSUInteger {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
_contactsManager = [[Environment getCurrent] contactsManager];
_storageManager = [TSStorageManager sharedManager];
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self) {
return self;
}
_contactsManager = [[Environment getCurrent] contactsManager];
_storageManager = [TSStorageManager sharedManager];
return self;
}
- (void)peekSetup {
_peek = YES;
[self setComposeOnOpen:NO];
@ -187,12 +217,11 @@ typedef enum : NSUInteger {
self.messageAdapterCache = [[NSCache alloc] init];
_showFingerprintDisplay =
self.showFingerprintGesture =
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(showFingerprint)];
_toggleContactPhoneDisplay =
self.toggleContactPhoneGesture =
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleContactPhone)];
_toggleContactPhoneDisplay.numberOfTapsRequired = 1;
_attachButton = [[UIButton alloc] init];
[_attachButton setFrame:CGRectMake(0,
@ -521,8 +550,8 @@ typedef enum : NSUInteger {
if ([label.text isEqualToString:self.title]) {
[self.navView setUserInteractionEnabled:YES];
[aView setUserInteractionEnabled:YES];
[aView addGestureRecognizer:_showFingerprintDisplay];
[aView addGestureRecognizer:_toggleContactPhoneDisplay];
[aView addGestureRecognizer:self.showFingerprintGesture];
[aView addGestureRecognizer:self.toggleContactPhoneGesture];
return;
}
}
@ -542,8 +571,8 @@ typedef enum : NSUInteger {
if ([label.text isEqualToString:self.title]) {
[self.navView setUserInteractionEnabled:NO];
[aView setUserInteractionEnabled:NO];
[aView removeGestureRecognizer:_showFingerprintDisplay];
[aView removeGestureRecognizer:_toggleContactPhoneDisplay];
[aView removeGestureRecognizer:self.showFingerprintGesture];
[aView removeGestureRecognizer:self.toggleContactPhoneGesture];
return;
}
}
@ -581,9 +610,25 @@ typedef enum : NSUInteger {
#pragma mark - Fingerprints
- (void)showFingerprint {
- (void)showFingerprint
{
// Show fingerprint for their most recently accepted identity
NSString *theirSignalId = self.thread.contactIdentifier;
NSData *theirIdentityKey = [self.storageManager identityKeyForRecipientId:theirSignalId];
[self showFingerprintWithTheirIdentityKey:theirIdentityKey theirSignalId:theirSignalId];
}
- (void)showFingerprintWithTheirIdentityKey:(NSData *)theirIdentityKey theirSignalId:(NSString *)theirSignalId
{
NSString *mySignalId = [self.storageManager localNumber];
NSData *myIdentityKey = [self.storageManager identityKeyPair].publicKey;
OWSFingerprint *fingerprint = [OWSFingerprint fingerprintWithMyStableId:mySignalId
myIdentityKey:myIdentityKey
theirStableId:theirSignalId
theirIdentityKey:theirIdentityKey];
[self markAllMessagesAsRead];
[self performSegueWithIdentifier:kFingerprintSegueIdentifier sender:self];
[self performSegueWithIdentifier:kFingerprintSegueIdentifier sender:fingerprint];
}
@ -591,8 +636,7 @@ typedef enum : NSUInteger {
_displayPhoneAsTitle = !_displayPhoneAsTitle;
if (!_thread.isGroupThread) {
Contact *contact =
[[[Environment getCurrent] contactsManager] latestContactForPhoneNumber:[self phoneNumberForThread]];
Contact *contact = [self.contactsManager latestContactForPhoneNumber:[self phoneNumberForThread]];
if (!contact) {
if (!(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(NSFoundationVersionNumber_iOS_9))) {
ABUnknownPersonViewController *view = [[ABUnknownPersonViewController alloc] init];
@ -614,7 +658,7 @@ typedef enum : NSUInteger {
[self.navigationController pushViewController:view animated:YES];
}
} else {
CNContactStore *contactStore = [Environment getCurrent].contactsManager.contactStore;
CNContactStore *contactStore = self.contactsManager.contactStore;
CNMutableContact *cncontact = [[CNMutableContact alloc] init];
cncontact.phoneNumbers = @[
@ -697,7 +741,7 @@ typedef enum : NSUInteger {
- (void)callAction {
if ([self canCall]) {
PhoneNumber *number = [self phoneNumberForThread];
Contact *contact = [[Environment.getCurrent contactsManager] latestContactForPhoneNumber:number];
Contact *contact = [self.contactsManager latestContactForPhoneNumber:number];
[Environment.phoneManager initiateOutgoingCallToContact:contact atRemoteNumber:number];
} else {
DDLogWarn(@"Tried to initiate a call but thread is not callable.");
@ -1023,8 +1067,7 @@ typedef enum : NSUInteger {
}
if ([self.thread isKindOfClass:[TSGroupThread class]]) {
NSString *name = [[Environment getCurrent].contactsManager nameStringForPhoneIdentifier:msg.senderId];
name = name ? name : msg.senderId;
NSString *name = [self.contactsManager nameStringForPhoneIdentifier:msg.senderId];
if (!name) {
name = @"";
@ -1391,75 +1434,72 @@ typedef enum : NSUInteger {
}];
}
- (void)handleErrorMessageTap:(TSErrorMessage *)message {
- (void)handleErrorMessageTap:(TSErrorMessage *)message
{
if ([message isKindOfClass:[TSInvalidIdentityKeyErrorMessage class]]) {
TSInvalidIdentityKeyErrorMessage *errorMessage = (TSInvalidIdentityKeyErrorMessage *)message;
NSString *newKeyFingerprint = [errorMessage newIdentityFingerprint];
NSString *keyOwner;
if ([message isKindOfClass:[TSInvalidIdentityKeySendingErrorMessage class]]) {
TSInvalidIdentityKeySendingErrorMessage *m = (TSInvalidIdentityKeySendingErrorMessage *)message;
keyOwner = [[[Environment getCurrent] contactsManager] nameStringForPhoneIdentifier:m.recipientId];
} else {
keyOwner = [self.thread name];
}
NSString *messageString = [NSString
stringWithFormat:NSLocalizedString(@"ACCEPT_IDENTITYKEY_QUESTION", @""), keyOwner, newKeyFingerprint];
NSArray *actions = @[
NSLocalizedString(@"ACCEPT_IDENTITYKEY_BUTTON", @""),
NSLocalizedString(@"COPY_IDENTITYKEY_BUTTON", @"")
];
[self dismissKeyBoard];
[DJWActionSheet showInView:self.parentViewController.view
withTitle:messageString
cancelButtonTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"")
destructiveButtonTitle:NSLocalizedString(@"TXT_DELETE_TITLE", @"")
otherButtonTitles:actions
tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
DDLogDebug(@"User Cancelled");
} else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) {
[self.editingDatabaseConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message removeWithTransaction:transaction];
}];
} else {
switch (tappedButtonIndex) {
case 0:
[errorMessage acceptNewIdentityKey];
break;
case 1:
[[UIPasteboard generalPasteboard] setString:newKeyFingerprint];
break;
default:
break;
}
}
}];
[self tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)message];
}
}
- (void)tappedInvalidIdentityKeyErrorMessage:(TSInvalidIdentityKeyErrorMessage *)errorMessage
{
NSString *keyOwner = [self.contactsManager nameStringForPhoneIdentifier:errorMessage.theirSignalId];
NSString *titleFormat = NSLocalizedString(@"SAFETY_NUMBERS_ACTIONSHEET_TITLE", @"Action sheet heading");
NSString *titleText = [NSString stringWithFormat:titleFormat, keyOwner];
NSArray *actions = @[
NSLocalizedString(@"SHOW_SAFETY_NUMBER_ACTION", @"Action sheet item"),
NSLocalizedString(@"ACCEPT_NEW_IDENTITY_ACTION", @"Action sheet item")
];
[self dismissKeyBoard];
[DJWActionSheet showInView:self.parentViewController.view
withTitle:titleText
cancelButtonTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"")
destructiveButtonTitle:nil
otherButtonTitles:actions
tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
DDLogDebug(@"%@ Remote Key Changed actions: Tapped cancel", self.tag);
} else {
switch (tappedButtonIndex) {
case 0:
DDLogInfo(@"%@ Remote Key Changed actions: Show fingerprint display", self.tag);
[self showFingerprintWithTheirIdentityKey:errorMessage.newIdentityKey
theirSignalId:errorMessage.theirSignalId];
break;
case 1:
DDLogInfo(@"%@ Remote Key Changed actions: Accepted new identity key", self.tag);
[errorMessage acceptNewIdentityKey];
break;
default:
DDLogInfo(@"%@ Remote Key Changed actions: Unhandled button pressed: %d",
self.tag,
(int)tappedButtonIndex);
break;
}
}
}];
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:kFingerprintSegueIdentifier]) {
FingerprintViewController *vc = [segue destinationViewController];
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[vc configWithThread:self.thread];
}];
if ([sender isKindOfClass:[OWSFingerprint class]]) {
OWSFingerprint *fingerprint = (OWSFingerprint *)sender;
NSString *contactName = [self.contactsManager nameStringForPhoneIdentifier:fingerprint.theirStableId];
[vc configureWithFingerprint:fingerprint contactName:contactName];
} else {
DDLogError(@"%@ Attempting to segueu to fingerprint VC without a valid fingerprint: %@", self.tag, sender);
}
} else if ([segue.identifier isEqualToString:kUpdateGroupSegueIdentifier]) {
NewGroupViewController *vc = [segue destinationViewController];
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[vc configWithThread:(TSGroupThread *)self.thread];
}];
[vc configWithThread:(TSGroupThread *)self.thread];
} else if ([segue.identifier isEqualToString:kShowGroupMembersSegue]) {
ShowGroupMembersViewController *vc = [segue destinationViewController];
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[vc configWithThread:(TSGroupThread *)self.thread];
}];
[vc configWithThread:(TSGroupThread *)self.thread];
}
}
@ -1715,7 +1755,7 @@ typedef enum : NSUInteger {
- (YapDatabaseConnection *)uiDatabaseConnection {
NSAssert([NSThread isMainThread], @"Must access uiDatabaseConnection on main thread!");
if (!_uiDatabaseConnection) {
_uiDatabaseConnection = [[TSStorageManager sharedManager] newDatabaseConnection];
_uiDatabaseConnection = [self.storageManager newDatabaseConnection];
[_uiDatabaseConnection beginLongLivedReadTransaction];
}
return _uiDatabaseConnection;
@ -1723,7 +1763,7 @@ typedef enum : NSUInteger {
- (YapDatabaseConnection *)editingDatabaseConnection {
if (!_editingDatabaseConnection) {
_editingDatabaseConnection = [[TSStorageManager sharedManager] newDatabaseConnection];
_editingDatabaseConnection = [self.storageManager newDatabaseConnection];
}
return _editingDatabaseConnection;
}
@ -2142,4 +2182,14 @@ typedef enum : NSUInteger {
return @[];
}
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end

View File

@ -8,4 +8,6 @@
@property OWSLinkedDevicesTableViewController *linkedDevicesTableViewController;
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithString:(NSString *)string;
@end

View File

@ -28,8 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
// HACK to get full width preview layer
CGRect oldFrame = self.qrScanningView.frame;
self.qrScanningView.frame = CGRectMake(
oldFrame.origin.x, oldFrame.origin.y, self.view.frame.size.width, self.view.frame.size.height / 2.0 - 32.0);
[self.qrScanningController resizeViews];
oldFrame.origin.x, oldFrame.origin.y, self.view.frame.size.width, self.view.frame.size.height / 2.0f - 32.0f);
// END HACK to get full width preview layer
self.scanningInstructionsLabel.text = NSLocalizedString(@"LINK_DEVICE_SCANNING_INSTRUCTIONS",
@ -43,6 +42,12 @@ NS_ASSUME_NONNULL_BEGIN
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.qrScanningController startCapture];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender
{
if ([segue.identifier isEqualToString:@"embedDeviceQRScanner"]) {

View File

@ -307,7 +307,7 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
OWSDevice *device = [self deviceForRowAtIndexPath:indexPath];
[self touchedUnlinkControlForDevice:device
success:^{
DDLogInfo(@"Removing unlinked device with deviceId: %ld", device.deviceId);
DDLogInfo(@"Removing unlinked device with deviceId: %ld", (long)device.deviceId);
[device remove];
}];
}

View File

@ -2,27 +2,23 @@
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>
#import <ZXingObjC/ZXingObjC.h>
@class OWSQRCodeScanningViewController;
@protocol OWSQRScannerDelegate
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithString:(NSString *)scannedString;
@optional
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithString:(NSString *)string;
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data;
@end
@interface OWSQRCodeScanningViewController : UIViewController <AVCaptureMetadataOutputObjectsDelegate>
@interface OWSQRCodeScanningViewController
: UIViewController <AVCaptureMetadataOutputObjectsDelegate, ZXCaptureDelegate>
@property (nonatomic, strong) AVCaptureSession *session;
@property (nonatomic, strong) AVCaptureDevice *device;
@property (nonatomic, strong) AVCaptureDeviceInput *input;
@property (nonatomic, strong) AVCaptureMetadataOutput *output;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *prevLayer;
@property (nonatomic, strong) UIView *highlightView;
@property (nonatomic, weak) UIViewController<OWSQRScannerDelegate> *scanDelegate;
// HACK to resize views after embedding. Better would be to specify layout of preview layer as constraints.
- (void)resizeViews;
- (void)startCapture;
@end

View File

@ -2,99 +2,172 @@
#import "OWSQRCodeScanningViewController.h"
#import "UIColor+OWS.h"
//#import <ZXingObjC/ZXingObjC.h>
@interface OWSQRCodeScanningViewController ()
@property (nonatomic) BOOL captureEnabled;
@property (nonatomic, strong) ZXCapture *capture;
@property UIView *maskingView;
@property CALayer *maskingLayer;
@end
@implementation OWSQRCodeScanningViewController
- (void)dealloc
{
[self.capture.layer removeFromSuperlayer];
}
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
_captureEnabled = NO;
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self) {
return self;
}
_captureEnabled = NO;
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = NSLocalizedString(@"SCAN_KEY", @"");
self.highlightView = [[UIView alloc] init];
self.highlightView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin
| UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
self.highlightView.layer.borderColor = [UIColor ows_greenColor].CGColor;
self.highlightView.layer.borderWidth = 4;
[self.view addSubview:self.highlightView];
self.session = [[AVCaptureSession alloc] init];
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:&error];
if (self.input) {
[self.session addInput:self.input];
} else {
DDLogDebug(@"Error: %@", error);
}
self.output = [[AVCaptureMetadataOutput alloc] init];
[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
[self.session addOutput:self.output];
self.output.metadataObjectTypes = [self.output availableMetadataObjectTypes];
self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
self.prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer insertSublayer:self.prevLayer atIndex:0];
[self.view bringSubviewToFront:self.highlightView];
self.maskingView = [[UIView alloc] initWithFrame:self.view.frame];
[self.view addSubview:self.maskingView];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self resizeViews];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.session startRunning];
}
- (void)resizeViews
{
self.prevLayer.frame = self.view.bounds;
}
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection
{
CGRect highlightViewRect = CGRectZero;
AVMetadataMachineReadableCodeObject *barCodeObject;
NSString *detectionString = nil;
NSArray *barCodeTypes = @[ AVMetadataObjectTypeQRCode ];
for (AVMetadataObject *metadata in metadataObjects) {
for (NSString *type in barCodeTypes) {
if ([metadata.type isEqualToString:type]) {
barCodeObject = (AVMetadataMachineReadableCodeObject *)[self.prevLayer
transformedMetadataObjectForMetadataObject:(AVMetadataMachineReadableCodeObject *)metadata];
highlightViewRect = barCodeObject.bounds;
detectionString = [(AVMetadataMachineReadableCodeObject *)metadata stringValue];
break;
}
}
if (detectionString != nil) {
[self didDetectQRCodeWithString:detectionString];
[self.session stopRunning];
break;
}
if (self.captureEnabled) {
[self startCapture];
}
self.highlightView.frame = highlightViewRect;
}
- (void)didDetectQRCodeWithString:(NSString *)string
- (void)viewDidLayoutSubviews
{
DDLogDebug(@"Scanned QRCode with string value: %@", string);
[super viewDidLayoutSubviews];
[self layoutMaskingView];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self stopCapture];
}
- (void)layoutMaskingView
{
self.maskingView.frame = self.view.frame;
if (self.maskingLayer) {
[self.maskingLayer removeFromSuperlayer];
}
self.maskingLayer = [self buildCircularMaskingLayer];
[self.maskingView.layer addSublayer:self.maskingLayer];
}
- (void)startCapture
{
self.captureEnabled = YES;
if (!self.capture) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.capture = [[ZXCapture alloc] init];
self.capture.camera = self.capture.back;
self.capture.focusMode = AVCaptureFocusModeContinuousAutoFocus;
self.capture.layer.frame = self.view.frame;
self.capture.delegate = self;
dispatch_async(dispatch_get_main_queue(), ^{
[self.view.layer addSublayer:self.capture.layer];
[self.view bringSubviewToFront:self.maskingView];
});
});
}
[self.capture start];
}
- (void)stopCapture
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.capture stop];
});
}
- (CAShapeLayer *)buildCircularMaskingLayer
{
// Add a circular mask
UIBezierPath *path = [UIBezierPath
bezierPathWithRoundedRect:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)
cornerRadius:0];
CGFloat verticalMargin = 8.0;
CGFloat radius = self.view.frame.size.height / 2.0f - verticalMargin;
// Center the circle's bounding rectangle
CGFloat horizontalMargin = (self.view.frame.size.width - 2.0f * radius) / 2.0f;
UIBezierPath *circlePath = [UIBezierPath
bezierPathWithRoundedRect:CGRectMake(horizontalMargin, verticalMargin, 2.0f * radius, 2.0f * radius)
cornerRadius:radius];
[path appendPath:circlePath];
[path setUsesEvenOddFillRule:YES];
CAShapeLayer *fillLayer = [CAShapeLayer layer];
fillLayer.path = path.CGPath;
fillLayer.fillRule = kCAFillRuleEvenOdd;
fillLayer.fillColor = [UIColor grayColor].CGColor;
fillLayer.opacity = 0.5;
return fillLayer;
}
- (void)captureResult:(ZXCapture *)capture result:(ZXResult *)result
{
[self stopCapture];
// TODO bounding rectangle
// Vibrate
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
if (self.scanDelegate) {
[self.scanDelegate controller:self didDetectQRCodeWithString:string];
if ([self.scanDelegate respondsToSelector:@selector(controller:didDetectQRCodeWithData:)]) {
DDLogInfo(@"%@ Scanned Data Code.", self.tag);
ZXByteArray *byteArray = result.resultMetadata[@(kResultMetadataTypeByteSegments)][0];
NSData *decodedData = [NSData dataWithBytes:byteArray.array length:byteArray.length];
[self.scanDelegate controller:self didDetectQRCodeWithData:decodedData];
}
if ([self.scanDelegate respondsToSelector:@selector(controller:didDetectQRCodeWithString:)]) {
DDLogInfo(@"%@ Scanned String Code.", self.tag);
[self.scanDelegate controller:self didDetectQRCodeWithString:result.text];
}
}
}
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end

View File

@ -1,16 +0,0 @@
//
// PresentIdentityQRCodeViewController.h
// Signal-iOS
//
// Created by Christine Corbett Moran on 3/30/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface PresentIdentityQRCodeViewController : UIViewController
@property (nonatomic, strong) IBOutlet UIImageView *qrCodeView;
@property (nonatomic, strong) IBOutlet UILabel *yourFingerprintLabel;
@property (nonatomic, strong) NSData *identityKey;
@end

View File

@ -1,41 +0,0 @@
//
// PresentIdentityQRCodeViewController.m
// Signal-iOS
//
// Created by Christine Corbett Moran on 3/30/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "NSData+Base64.h"
#import "PresentIdentityQRCodeViewController.h"
#import "UIImage+normalizeImage.h"
@implementation PresentIdentityQRCodeViewController
- (void)viewDidLoad {
[super viewDidLoad];
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[filter setDefaults];
[filter setValue:[[self.identityKey base64EncodedString] dataUsingEncoding:NSUTF8StringEncoding]
forKey:@"inputMessage"];
CIImage *outputImage = [filter outputImage];
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]];
UIImage *image = [UIImage imageWithCGImage:cgImage scale:1. orientation:UIImageOrientationUp];
// Resize without interpolating
UIImage *resized = [image resizedWithQuality:kCGInterpolationNone rate:5.0];
self.qrCodeView.image = resized;
_yourFingerprintLabel.text = NSLocalizedString(@"FINGERPRINT_YOURS", @"");
CGImageRelease(cgImage);
}
@end

View File

@ -12,21 +12,13 @@
#import "DJWActionSheet+OWS.h"
#import "Environment.h"
#import "PreferencesUtil.h"
#import "TSFingerprintGenerator.h"
#import "UIUtil.h"
@interface PrivacySettingsTableViewController ()
@property (nonatomic, strong) UITableViewCell *enableScreenSecurityCell;
@property (nonatomic, strong) UITableViewCell *clearHistoryLogCell;
@property (nonatomic, strong) UITableViewCell *fingerprintCell;
@property (nonatomic, strong) UITableViewCell *shareFingerprintCell;
@property (nonatomic, strong) UISwitch *enableScreenSecuritySwitch;
@property (nonatomic, strong) UILabel *fingerprintLabel;
@property (nonatomic, strong) NSTimer *copiedTimer;
@property (nonatomic, strong) UITableViewCell *clearHistoryLogCell;
@end
@ -51,51 +43,25 @@
// Enable Screen Security Cell
self.enableScreenSecurityCell = [[UITableViewCell alloc] init];
self.enableScreenSecurityCell.textLabel.text = NSLocalizedString(@"SETTINGS_SCREEN_SECURITY", @"");
self.enableScreenSecuritySwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
self.enableScreenSecurityCell.accessoryView = self.enableScreenSecuritySwitch;
self.enableScreenSecurityCell.userInteractionEnabled = YES;
[self.enableScreenSecuritySwitch setOn:[Environment.preferences screenSecurityIsEnabled]];
[self.enableScreenSecuritySwitch addTarget:self
action:@selector(didToggleScreenSecuritySwitch:)
forControlEvents:UIControlEventTouchUpInside];
// Clear History Log Cell
self.clearHistoryLogCell = [[UITableViewCell alloc] init];
self.clearHistoryLogCell.textLabel.text = NSLocalizedString(@"SETTINGS_CLEAR_HISTORY", @"");
self.clearHistoryLogCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Fingerprint Cell
self.fingerprintCell =
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Identifier"];
self.fingerprintCell.textLabel.text = NSLocalizedString(@"SETTINGS_FINGERPRINT", @"");
self.fingerprintCell.detailTextLabel.text = NSLocalizedString(@"SETTINGS_FINGERPRINT_COPY", nil);
self.fingerprintCell.detailTextLabel.textColor = [UIColor lightGrayColor];
self.fingerprintLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 25)];
self.fingerprintLabel.textColor = [UIColor lightGrayColor];
self.fingerprintLabel.font = [UIFont ows_regularFontWithSize:16.0f];
self.fingerprintLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
self.fingerprintCell.accessoryView = self.fingerprintLabel;
[self setValues];
[self subsribeToEvents];
}
- (void)subsribeToEvents {
[self.enableScreenSecuritySwitch addTarget:self
action:@selector(didToggleSwitch:)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)setValues {
[self.enableScreenSecuritySwitch setOn:[Environment.preferences screenSecurityIsEnabled]];
self.fingerprintLabel.text =
[TSFingerprintGenerator getFingerprintForDisplay:[[TSStorageManager sharedManager] identityKeyPair].publicKey];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
@ -104,8 +70,9 @@
return 1;
case 1:
return 1;
case 2:
return 1;
// TODO: optionally non-blocking
// case 2:
// return 1;
default:
return 0;
}
@ -126,13 +93,9 @@
return self.enableScreenSecurityCell;
case 1:
return self.clearHistoryLogCell;
case 2:
switch (indexPath.row) {
case 0:
return self.fingerprintCell;
case 1:
return self.shareFingerprintCell;
}
// TODO - safetynumber settings
// case 2:
// return [UITableViewCell new];
}
return nil;
@ -141,11 +104,11 @@
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
switch (section) {
case 0:
return NSLocalizedString(@"SETTINGS_SECURITY_TITLE", @"");
return NSLocalizedString(@"SETTINGS_SECURITY_TITLE", @"Section header");
case 1:
return NSLocalizedString(@"SETTINGS_HISTORYLOG_TITLE", @"");
return NSLocalizedString(@"SETTINGS_HISTORYLOG_TITLE", @"Section header");
case 2:
return NSLocalizedString(@"SETTINGS_FINGERPRINT", @"");
return NSLocalizedString(@"SETTINGS_PRIVACY_VERIFICATION_TITLE", @"Section header");
default:
return nil;
}
@ -174,31 +137,6 @@
break;
}
case 2:
switch (indexPath.row) {
case 0: {
// Timer to change label to copied (NSTextAttachment checkmark)
if (self.copiedTimer == nil) {
self.copiedTimer = [NSTimer scheduledTimerWithTimeInterval:2.0f
target:self
selector:@selector(endTimer:)
userInfo:nil
repeats:NO];
self.fingerprintCell.detailTextLabel.text =
NSLocalizedString(@"SETTINGS_FINGERPRINT_COPY_SUCCESS", @"");
} else {
self.fingerprintCell.detailTextLabel.text =
NSLocalizedString(@"SETTINGS_FINGERPRINT_COPY", nil);
}
[[UIPasteboard generalPasteboard] setString:self.fingerprintLabel.text];
break;
}
default:
break;
}
break;
default:
break;
}
@ -206,16 +144,23 @@
#pragma mark - Toggle
- (void)didToggleSwitch:(UISwitch *)sender {
[Environment.preferences setScreenSecurity:self.enableScreenSecuritySwitch.isOn];
- (void)didToggleScreenSecuritySwitch:(UISwitch *)sender
{
BOOL enabled = self.enableScreenSecuritySwitch.isOn;
DDLogInfo(@"%@ toggled screen security: %@", self.tag, enabled ? @"ON" : @"OFF");
[Environment.preferences setScreenSecurity:enabled];
}
#pragma mark - Timer
#pragma mark - Log util
- (void)endTimer:(id)sender {
self.fingerprintCell.detailTextLabel.text = NSLocalizedString(@"SETTINGS_FINGERPRINT_COPY", nil);
[self.copiedTimer invalidate];
self.copiedTimer = nil;
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end

View File

@ -1,14 +0,0 @@
//
// ScanIdentityBarcodeViewController.h
// Signal-iOS
//
// Created by Christine Corbett Moran on 3/29/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
#import "OWSQRCodeScanningViewController.h"
@interface ScanIdentityBarcodeViewController : OWSQRCodeScanningViewController
@property (nonatomic, strong) NSData *identityKey;
@end

View File

@ -1,53 +0,0 @@
//
// ScanIdentityBarcodeViewController.m
// Signal-iOS
//
// Created by Christine Corbett Moran on 3/29/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "ScanIdentityBarcodeViewController.h"
#import "NSData+Base64.h"
@implementation ScanIdentityBarcodeViewController
- (void)didDetectQRCodeWithString:(NSString *)string
{
NSData *data = [NSData dataFromBase64String:string];
NSString *dialogTitle;
NSString *dialogDescription;
if ([data isEqualToData:self.identityKey]) {
dialogTitle = NSLocalizedString(@"SCAN_KEY_VERIFIED_TITLE", @"");
dialogDescription = NSLocalizedString(@"SCAN_KEY_VERIFIED_TEXT", @"");
} else {
dialogTitle = NSLocalizedString(@"SCAN_KEY_CONFLICT_TITLE", @"");
dialogDescription = NSLocalizedString(@"SCAN_KEY_CONFLICT_TEXT", @"");
}
UIAlertController *controller = [UIAlertController alertControllerWithTitle:dialogTitle
message:dialogDescription
preferredStyle:UIAlertControllerStyleAlert];
[self
presentViewController:controller
animated:YES
completion:^{
[self performSelector:@selector(dismissScannerAfterSuccesfullScan) withObject:nil afterDelay:5];
}];
}
#pragma mark - Action
- (void)dismissScannerAfterSuccesfullScan {
[self dismissViewControllerAnimated:YES
completion:^{
[self closeButtonAction:nil];
}];
}
- (IBAction)closeButtonAction:(id)sender {
[self performSegueWithIdentifier:@"UnwindToIdentityKeyWasVerifiedSegue" sender:self];
}
@end

View File

@ -28,7 +28,7 @@
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qCf-bs-dBd" userLabel="textContainer">
<rect key="frame" x="0.0" y="42" width="320" height="48"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Info Message" textAlignment="center" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="OVa-Xw-5vl" customClass="JSQMessagesLabel">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Info Message" textAlignment="center" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" numberOfLines="0" translatesAutoresizingMaskIntoConstraints="NO" id="OVa-Xw-5vl" customClass="JSQMessagesLabel">
<rect key="frame" x="8" y="12" width="304" height="28"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="14" id="fed-2c-dqd"/>