Custom contact picker for invite flow
Preferred to the system contact picker because: 1. removes "group" clutter from header, unlikely to be used much. 2. can select while searching 3. fixes unified contact problem where e.g. If only one of your contact has a phone number, they appear disabled when choosing to invite via messaging, even though the other linked contact *does* have a phone number. 4. label users w/o email so it's clearer why they can't be selected Also: * Twitter share-image was too tall // FREEBIE
This commit is contained in:
parent
f9a60b622d
commit
f30c733ef3
|
@ -134,7 +134,7 @@ EXTERNAL SOURCES:
|
|||
|
||||
CHECKOUT OPTIONS:
|
||||
SignalServiceKit:
|
||||
:commit: df756423f24ac91dd69f45cc036c09771c15f6eb
|
||||
:commit: 3083e2929c7dcfe5c60e003dd77c310d26dab177
|
||||
:git: https://github.com/WhisperSystems/SignalServiceKit.git
|
||||
SocketRocket:
|
||||
:commit: 41b57bb2fc292a814f758441a05243eb38457027
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */; };
|
||||
450873C81D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */; };
|
||||
4516E3FF1DD2193B00DC4206 /* OWS101ExistingUsersBlockOnIdentityChange.m in Sources */ = {isa = PBXBuildFile; fileRef = 4516E3FE1DD2193B00DC4206 /* OWS101ExistingUsersBlockOnIdentityChange.m */; };
|
||||
451764271DE939F300EDB8B9 /* ContactsPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451764261DE939F300EDB8B9 /* ContactsPicker.swift */; };
|
||||
4517642A1DE939FD00EDB8B9 /* ContactCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 451764281DE939FD00EDB8B9 /* ContactCell.xib */; };
|
||||
4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451764291DE939FD00EDB8B9 /* ContactCell.swift */; };
|
||||
451DE9F81DC18C9500810E42 /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD81EE1DC030E7004C9430 /* AccountManager.swift */; };
|
||||
451DE9FD1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */; };
|
||||
451DE9FE1DC1A28200810E42 /* SyncPushTokensJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */; };
|
||||
|
@ -48,6 +51,7 @@
|
|||
459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */; };
|
||||
459C3F0D1C9B3A1B003ACF51 /* TSMessageAdapterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 459C3F0C1C9B3A1B003ACF51 /* TSMessageAdapterTest.m */; };
|
||||
45B201761DAECBFE00C461E0 /* HighlightableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45B201751DAECBFE00C461E0 /* HighlightableLabel.swift */; };
|
||||
45BD60821DE9547E00A8F436 /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45BD60811DE9547E00A8F436 /* Contacts.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
45BFFFA81D898AF0004A12A7 /* OWSStaleNotificationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 45BFFFA71D898AF0004A12A7 /* OWSStaleNotificationObserver.m */; };
|
||||
45BFFFA91D898AF0004A12A7 /* OWSStaleNotificationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 45BFFFA71D898AF0004A12A7 /* OWSStaleNotificationObserver.m */; };
|
||||
45C681B71D305A580050903A /* OWSCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C681B61D305A580050903A /* OWSCall.m */; };
|
||||
|
@ -551,6 +555,9 @@
|
|||
450873C91D9D86F4006B54F2 /* OWSExpirableMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSExpirableMessageView.h; sourceTree = "<group>"; };
|
||||
4516E3FD1DD2193B00DC4206 /* OWS101ExistingUsersBlockOnIdentityChange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWS101ExistingUsersBlockOnIdentityChange.h; path = Migrations/OWS101ExistingUsersBlockOnIdentityChange.h; sourceTree = "<group>"; };
|
||||
4516E3FE1DD2193B00DC4206 /* OWS101ExistingUsersBlockOnIdentityChange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWS101ExistingUsersBlockOnIdentityChange.m; path = Migrations/OWS101ExistingUsersBlockOnIdentityChange.m; sourceTree = "<group>"; };
|
||||
451764261DE939F300EDB8B9 /* ContactsPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsPicker.swift; sourceTree = "<group>"; };
|
||||
451764281DE939FD00EDB8B9 /* ContactCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContactCell.xib; sourceTree = "<group>"; };
|
||||
451764291DE939FD00EDB8B9 /* ContactCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactCell.swift; sourceTree = "<group>"; };
|
||||
451DE9F11DC1585F00810E42 /* PromiseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PromiseKit.framework; path = Carthage/Build/iOS/PromiseKit.framework; sourceTree = "<group>"; };
|
||||
451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SyncPushTokensJob.swift; path = Models/SyncPushTokensJob.swift; sourceTree = "<group>"; };
|
||||
4520D8D41D417D8E00123472 /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; };
|
||||
|
@ -602,6 +609,7 @@
|
|||
459C3F0C1C9B3A1B003ACF51 /* TSMessageAdapterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSMessageAdapterTest.m; path = "view controllers/Signals/TSMessageAdapters/TSMessageAdapterTest.m"; sourceTree = "<group>"; };
|
||||
45B201741DAECBFD00C461E0 /* Signal-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Signal-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
45B201751DAECBFE00C461E0 /* HighlightableLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighlightableLabel.swift; sourceTree = "<group>"; };
|
||||
45BD60811DE9547E00A8F436 /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; };
|
||||
45BFFFA61D898AF0004A12A7 /* OWSStaleNotificationObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSStaleNotificationObserver.h; path = Observers/OWSStaleNotificationObserver.h; sourceTree = "<group>"; };
|
||||
45BFFFA71D898AF0004A12A7 /* OWSStaleNotificationObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSStaleNotificationObserver.m; path = Observers/OWSStaleNotificationObserver.m; sourceTree = "<group>"; };
|
||||
45C681B51D305A580050903A /* OWSCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCall.h; sourceTree = "<group>"; };
|
||||
|
@ -1153,6 +1161,7 @@
|
|||
456C38961DC7B882007536A7 /* PromiseKit.framework in Frameworks */,
|
||||
4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */,
|
||||
B6B226971BE4B7D200860F4D /* ContactsUI.framework in Frameworks */,
|
||||
45BD60821DE9547E00A8F436 /* Contacts.framework in Frameworks */,
|
||||
B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */,
|
||||
FC3BD9881A30A790005B96BB /* Social.framework in Frameworks */,
|
||||
FCB11D8C1A129A76002F93FB /* CoreMedia.framework in Frameworks */,
|
||||
|
@ -1826,6 +1835,7 @@
|
|||
76EB050C18170B33006006FC /* InCallViewController.m */,
|
||||
458E382F1D6682450094BD24 /* OWSQRCodeScanningViewController.h */,
|
||||
458E38301D6682450094BD24 /* OWSQRCodeScanningViewController.m */,
|
||||
451764261DE939F300EDB8B9 /* ContactsPicker.swift */,
|
||||
45514DE11DDFA183003EFF90 /* InviteFlow.swift */,
|
||||
);
|
||||
name = "View Controllers";
|
||||
|
@ -1854,6 +1864,8 @@
|
|||
FCAC963E19FEF99A0046DFC5 /* InboxTableViewCell.m */,
|
||||
76EB052E18170B33006006FC /* ContactTableViewCell.h */,
|
||||
76EB052F18170B33006006FC /* ContactTableViewCell.m */,
|
||||
451764291DE939FD00EDB8B9 /* ContactCell.swift */,
|
||||
451764281DE939FD00EDB8B9 /* ContactCell.xib */,
|
||||
76EB053818170B33006006FC /* xibs */,
|
||||
459311FA1D75C948008DD4F0 /* OWSDeviceTableViewCell.h */,
|
||||
459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */,
|
||||
|
@ -2233,6 +2245,7 @@
|
|||
D221A08C169C9E5E00537ABF /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
45BD60811DE9547E00A8F436 /* Contacts.framework */,
|
||||
451DE9F11DC1585F00810E42 /* PromiseKit.framework */,
|
||||
4520D8D41D417D8E00123472 /* Photos.framework */,
|
||||
B6B226961BE4B7D200860F4D /* ContactsUI.framework */,
|
||||
|
@ -2651,6 +2664,7 @@
|
|||
AD83FF401A73426500B5C81A /* audio_pause_button_blue@2x.png in Resources */,
|
||||
B66DBF4A19D5BBC8006EA940 /* Images.xcassets in Resources */,
|
||||
70B8FEE21909FE360042E3F0 /* 171756__nenadsimic__picked-coin-echo-2.wav in Resources */,
|
||||
4517642A1DE939FD00EDB8B9 /* ContactCell.xib in Resources */,
|
||||
AD83FF431A73426500B5C81A /* audio_play_button@2x.png in Resources */,
|
||||
45CB2FA81CB7146C00E1B343 /* Launch Screen.storyboard in Resources */,
|
||||
B633C5C31A1D190B0059AC12 /* mute_off@2x.png in Resources */,
|
||||
|
@ -2926,6 +2940,8 @@
|
|||
76EB05EA18170B33006006FC /* CallProgress.m in Sources */,
|
||||
458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */,
|
||||
FCFA64B41A24F3880007FB87 /* UIColor+OWS.m in Sources */,
|
||||
4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */,
|
||||
451764271DE939F300EDB8B9 /* ContactsPicker.swift in Sources */,
|
||||
76EB05C218170B33006006FC /* DhPacketSharedSecretHashes.m in Sources */,
|
||||
B6C93C4E199567AD00EDF894 /* DebugLogger.m in Sources */,
|
||||
76EB063218170B33006006FC /* Crc32.m in Sources */,
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB |
BIN
Signal/Images.xcassets/twitter_sharing_image.imageset/logo_with_background.png
vendored
Normal file
BIN
Signal/Images.xcassets/twitter_sharing_image.imageset/logo_with_background.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
BIN
Signal/Images.xcassets/twitter_sharing_image.imageset/logo_with_background@2x.png
vendored
Normal file
BIN
Signal/Images.xcassets/twitter_sharing_image.imageset/logo_with_background@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
Signal/Images.xcassets/twitter_sharing_image.imageset/logo_with_background@3x.png
vendored
Normal file
BIN
Signal/Images.xcassets/twitter_sharing_image.imageset/logo_with_background@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
|
@ -38,7 +38,7 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.6.6.5</string>
|
||||
<string>2.6.6.6</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LOGS_EMAIL</key>
|
||||
|
|
|
@ -5,6 +5,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@class TSThread;
|
||||
@class OWSContactsManager;
|
||||
@class UIImage;
|
||||
|
||||
@interface OWSAvatarBuilder : NSObject
|
||||
|
||||
|
|
|
@ -3,11 +3,17 @@
|
|||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "Environment.h"
|
||||
#import "OWSContactAvatarBuilder.h"
|
||||
#import "OWSContactsManager.h"
|
||||
#import "OWSLogger.h"
|
||||
#import "PhoneNumber.h"
|
||||
#import "PropertyListPreferences.h"
|
||||
#import "PushManager.h"
|
||||
#import "RPAccountManager.h"
|
||||
#import "UIFont+OWS.h"
|
||||
#import "UIUtil.h"
|
||||
#import <SignalServiceKit/Contact.h>
|
||||
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
|
||||
#import <SignalServiceKit/OWSEndSessionMessage.h>
|
||||
#import <SignalServiceKit/OWSError.h>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="No Messages :( Tap Compose to send a message or invite a friend to Signal" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Srx-i1-WhD">
|
||||
<rect key="frame" x="37.5" y="106.5" width="300" height="200"/>
|
||||
<rect key="frame" x="38" y="107" width="300" height="200"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="200" id="GEd-dY-d8r"/>
|
||||
<constraint firstAttribute="width" constant="300" id="siA-1a-pO1"/>
|
||||
|
@ -143,10 +143,10 @@
|
|||
<blurEffect style="dark"/>
|
||||
</visualEffectView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8DU-2J-HAz" userLabel="Scanner Container">
|
||||
<rect key="frame" x="0.0" y="-333.5" width="375" height="333.5"/>
|
||||
<rect key="frame" x="0.0" y="-334" width="375" height="334"/>
|
||||
<subviews>
|
||||
<containerView hidden="YES" opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MhA-kC-nBb" userLabel="Scanner">
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="269.5"/>
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="270"/>
|
||||
<connections>
|
||||
<segue destination="EFG-13-FgR" kind="embed" identifier="embedIdentityQRScanner" id="0sF-Is-2kw"/>
|
||||
</connections>
|
||||
|
@ -160,19 +160,19 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="b7W-j4-3S1" userLabel="QR Container">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="333.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="334"/>
|
||||
<subviews>
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scan the QR Code on your contact's device." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="NXP-Ue-iml">
|
||||
<rect key="frame" x="16" y="16" width="343" height="20.5"/>
|
||||
<rect key="frame" x="16" y="16" width="343" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lA7-b4-o6C" userLabel="QR Frame">
|
||||
<rect key="frame" x="61.5" y="72" width="253.5" height="253.5"/>
|
||||
<rect key="frame" x="60.5" y="72" width="254" height="254"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="btnQRShow--white" highlightedImage="btnQRShow--white-1" translatesAutoresizingMaskIntoConstraints="NO" id="YOs-e5-ee3" userLabel="Privacy Verification QR">
|
||||
<rect key="frame" x="41.5" y="43" width="169" height="168.5"/>
|
||||
<rect key="frame" x="42.5" y="43" width="169" height="169"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="YOs-e5-ee3" secondAttribute="height" multiplier="1:1" id="lN0-BE-w2c"/>
|
||||
</constraints>
|
||||
|
@ -198,7 +198,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="B4o-Rc-z68" userLabel="Instructions Container">
|
||||
<rect key="frame" x="0.0" y="333.5" width="375" height="289.5"/>
|
||||
<rect key="frame" x="0.0" y="334" width="375" height="289"/>
|
||||
<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" customClass="OWSHighlightableLabel">
|
||||
<rect key="frame" x="36" y="0.0" width="303" height="70"/>
|
||||
|
@ -216,7 +216,7 @@
|
|||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scan the code on your contact's device, 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="343" height="109.5"/>
|
||||
<rect key="frame" x="16" y="78" width="343" height="109"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44" id="bif-t1-qQO"/>
|
||||
</constraints>
|
||||
|
@ -225,7 +225,7 @@
|
|||
<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="117" y="207.5" width="141" height="70"/>
|
||||
<rect key="frame" x="117" y="207" width="141" height="70"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="70" id="g0E-XL-o8K"/>
|
||||
</constraints>
|
||||
|
@ -342,13 +342,13 @@
|
|||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Edward Snowden" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qCt-Cp-bFX" userLabel="Name">
|
||||
<rect key="frame" x="84" y="15" width="259" height="26.5"/>
|
||||
<rect key="frame" x="84" y="15" width="259" height="27"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle2"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="+1 323-555-1234" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tmV-r0-09O" userLabel="Signal ID">
|
||||
<rect key="frame" x="84" y="41.5" width="259" height="18"/>
|
||||
<rect key="frame" x="84" y="42" width="259" height="18"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -407,7 +407,7 @@
|
|||
<rect key="frame" x="0.0" y="144" width="375" height="108"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="U6h-Xo-HEv" id="Nmz-2u-fOY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="107"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="107.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Disappearing Messages" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="qbY-qJ-enK" userLabel="Disappearing Messages">
|
||||
|
@ -420,7 +420,7 @@
|
|||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0dO-mx-W3I" userLabel="Disappearing Messages Switch">
|
||||
<rect key="frame" x="310" y="6.5" width="51" height="31"/>
|
||||
<rect key="frame" x="310" y="7" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="disappearingMessagesSwitchValueDidChange:" destination="4oU-Rv-yJi" eventType="valueChanged" id="3fF-JA-69e"/>
|
||||
</connections>
|
||||
|
@ -434,7 +434,7 @@
|
|||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="When enabled, messages sent and received in this conversation will disappear after they have been seen." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="zIg-K4-8lV">
|
||||
<rect key="frame" x="64" y="40" width="287" height="59.5"/>
|
||||
<rect key="frame" x="64" y="40" width="287" height="60"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -460,7 +460,7 @@
|
|||
<rect key="frame" x="0.0" y="252" width="375" height="76"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3dL-aW-P1A" id="2a2-Po-p8O">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="75"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="75.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Messages disappear after 8 hours." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="c6Q-rV-1LO" userLabel="Keep messages for 8 hours.">
|
||||
|
@ -506,14 +506,14 @@
|
|||
<tableViewSection headerTitle="Group Management" id="z5m-Fe-GK8">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="NxZ-wa-xV9" style="IBUITableViewCellStyleDefault" id="XHr-b6-Gvn" userLabel="Update Group">
|
||||
<rect key="frame" x="0.0" y="385" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="384" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="XHr-b6-Gvn" id="Epj-vT-UYL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Update Group" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="NxZ-wa-xV9">
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
|
@ -526,14 +526,14 @@
|
|||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="geN-YN-TQg" style="IBUITableViewCellStyleDefault" id="w57-rz-BWN" userLabel="Leave Group">
|
||||
<rect key="frame" x="0.0" y="429" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="428" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="w57-rz-BWN" id="Pgy-Fc-U25">
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Leave Group" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="geN-YN-TQg">
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
|
@ -543,14 +543,14 @@
|
|||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Zml-Zn-2fd" style="IBUITableViewCellStyleDefault" id="Dnq-Ko-46l" userLabel="Group Members">
|
||||
<rect key="frame" x="0.0" y="473" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="472" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Dnq-Ko-46l" id="VRQ-31-E5Y">
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Group Members" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Zml-Zn-2fd">
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
|
@ -608,7 +608,7 @@
|
|||
<rect key="frame" x="0.0" y="22" width="375" 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="375" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
|
@ -769,7 +769,7 @@
|
|||
<rect key="frame" x="0.0" y="567" width="375" height="100"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F2a-2h-UK2" userLabel="Hang Up Button">
|
||||
<rect key="frame" x="147.5" y="0.0" width="80" height="80"/>
|
||||
<rect key="frame" x="148" y="0.0" width="80" height="80"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="80" id="ToV-Ks-2i9"/>
|
||||
<constraint firstAttribute="height" constant="80" id="ifY-o4-2Pv"/>
|
||||
|
@ -906,14 +906,14 @@
|
|||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<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="56" width="375" height="72"/>
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" 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="375" height="71"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="71.5"/>
|
||||
<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">
|
||||
<rect key="frame" x="17" y="8" width="135.5" height="20"/>
|
||||
<rect key="frame" x="17" y="8" width="136" height="20"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -925,7 +925,7 @@
|
|||
<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">
|
||||
<rect key="frame" x="17" y="46" width="147.5" height="17.5"/>
|
||||
<rect key="frame" x="17" y="46" width="148" height="18"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -950,21 +950,21 @@
|
|||
</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="128" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="127.5" width="375" 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="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<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="3" width="127" height="21"/>
|
||||
<rect key="frame" x="15" y="4" width="127" height="20.5"/>
|
||||
<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="custom" customColorSpace="sRGB"/>
|
||||
<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" width="88" height="16"/>
|
||||
<rect key="frame" x="15" y="24.5" width="88" height="16"/>
|
||||
<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="custom" customColorSpace="sRGB"/>
|
||||
|
@ -1019,7 +1019,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
|
||||
<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="375" height="95"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="95.5"/>
|
||||
<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">
|
||||
|
@ -1060,11 +1060,11 @@
|
|||
<rect key="frame" x="0.0" y="96" width="375" 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="375" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<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">
|
||||
<rect key="frame" x="15" y="-0.5" width="200" height="44.5"/>
|
||||
<rect key="frame" x="15" y="-0.5" width="200" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="nOw-0c-lAd"/>
|
||||
<constraint firstAttribute="width" constant="200" id="q6L-Sa-lrA"/>
|
||||
|
@ -1074,7 +1074,7 @@
|
|||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Connected" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tg3-dQ-odw">
|
||||
<rect key="frame" x="260" y="-0.5" width="100" height="44.5"/>
|
||||
<rect key="frame" x="260" y="-0.5" width="100" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="100" id="Lw2-Fv-sOC"/>
|
||||
<constraint firstAttribute="height" constant="44" id="uvH-QZ-iUw"/>
|
||||
|
@ -1100,11 +1100,11 @@
|
|||
<rect key="frame" x="0.0" y="140" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="a7a-qe-87x" id="gcU-Ld-alv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Invite Your Friends" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9FD-M0-WLe" userLabel="Invite Your Friends">
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
|
@ -1117,11 +1117,11 @@
|
|||
<rect key="frame" x="0.0" y="184" width="375" 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="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<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="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<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="custom" customColorSpace="sRGB"/>
|
||||
|
@ -1134,11 +1134,11 @@
|
|||
<rect key="frame" x="0.0" y="228" width="375" 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="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<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="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<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="custom" customColorSpace="sRGB"/>
|
||||
|
@ -1151,11 +1151,11 @@
|
|||
<rect key="frame" x="0.0" y="272" width="375" 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="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<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="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<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="custom" customColorSpace="sRGB"/>
|
||||
|
@ -1171,11 +1171,11 @@
|
|||
<rect key="frame" x="0.0" y="316" width="375" 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="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<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="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<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="custom" customColorSpace="sRGB"/>
|
||||
|
@ -1188,11 +1188,11 @@
|
|||
<rect key="frame" x="0.0" y="360" width="375" 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="342" height="43"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<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="325" height="43"/>
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<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="custom" customColorSpace="sRGB"/>
|
||||
|
@ -1209,7 +1209,7 @@
|
|||
<rect key="frame" x="0.0" y="404" width="375" 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="375" height="99"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="99.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4Mk-ly-6fq">
|
||||
|
@ -1295,18 +1295,18 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_devices_ios" translatesAutoresizingMaskIntoConstraints="NO" id="zl7-KG-ma4">
|
||||
<rect key="frame" x="86.5" y="64.5" width="171.5" height="114"/>
|
||||
<rect key="frame" x="86" y="64.5" width="171" height="114"/>
|
||||
<color key="tintColor" red="0.67450980392156867" green="0.67450980392156867" blue="0.67450980392156867" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="zl7-KG-ma4" secondAttribute="height" multiplier="3:2" id="RKt-hc-iWB"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GMf-cM-kQN" userLabel="Spacer">
|
||||
<rect key="frame" x="0.0" y="215" width="343" height="64.5"/>
|
||||
<rect key="frame" x="0.0" y="215.5" width="343" height="64"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<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="194.5" width="343" height="20.5"/>
|
||||
<rect key="frame" x="0.0" y="194.5" width="343" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -1390,32 +1390,34 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<prototypes>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ContactTableViewCell" rowHeight="48" id="Ld5-sX-pB8" customClass="ContactTableViewCell">
|
||||
<rect key="frame" x="0.0" y="22" width="375" height="48"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ContactTableViewCell" rowHeight="59" id="Ld5-sX-pB8" customClass="ContactTableViewCell">
|
||||
<rect key="frame" x="0.0" y="22" width="375" height="59"/>
|
||||
<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="375" height="47"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="58.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4zF-SU-q4z">
|
||||
<rect key="frame" x="67.5" y="8" width="299.5" height="31.5"/>
|
||||
<rect key="frame" x="68" y="8.5" width="299" height="42"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="empty-group-avatar" translatesAutoresizingMaskIntoConstraints="NO" id="11n-jp-FCg">
|
||||
<rect key="frame" x="16" y="4" width="39.5" height="39.5"/>
|
||||
<rect key="frame" x="16" y="9.5" width="40" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="11n-jp-FCg" secondAttribute="height" multiplier="1:1" id="CMp-Im-YMw"/>
|
||||
<constraint firstAttribute="height" constant="40" id="QYQ-Ma-4Vc"/>
|
||||
<constraint firstAttribute="width" constant="40" id="jlW-F8-wog"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="4zF-SU-q4z" secondAttribute="trailing" id="0Af-5G-21G"/>
|
||||
<constraint firstItem="11n-jp-FCg" firstAttribute="leading" secondItem="EqP-87-4hZ" secondAttribute="leadingMargin" constant="8" id="GYw-2Z-sxC"/>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="11n-jp-FCg" secondAttribute="bottom" constant="-4" id="XTB-Kg-dNU"/>
|
||||
<constraint firstItem="4zF-SU-q4z" firstAttribute="leading" secondItem="11n-jp-FCg" secondAttribute="trailing" constant="12" id="ZiW-nb-biK"/>
|
||||
<constraint firstItem="11n-jp-FCg" firstAttribute="top" secondItem="EqP-87-4hZ" secondAttribute="topMargin" constant="-4" id="pYu-uo-erC"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="11n-jp-FCg" secondAttribute="bottom" constant="6" id="d2J-3C-boc"/>
|
||||
<constraint firstItem="11n-jp-FCg" firstAttribute="top" relation="greaterThanOrEqual" secondItem="EqP-87-4hZ" secondAttribute="top" constant="6" id="f2o-9N-mNq"/>
|
||||
<constraint firstItem="11n-jp-FCg" firstAttribute="centerY" secondItem="EqP-87-4hZ" secondAttribute="centerY" id="kje-lH-syk"/>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="4zF-SU-q4z" secondAttribute="bottom" id="qZb-xR-JoS"/>
|
||||
<constraint firstItem="4zF-SU-q4z" firstAttribute="top" secondItem="EqP-87-4hZ" secondAttribute="topMargin" id="ruR-db-L1p"/>
|
||||
</constraints>
|
||||
|
@ -1463,7 +1465,7 @@
|
|||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Invite your friends!" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QGl-1D-W9D">
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="47"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" red="0.09412795243864841" green="0.43645224658557435" blue="0.71380208333333339" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
|
@ -1548,11 +1550,11 @@
|
|||
<rect key="frame" x="0.0" y="62" width="375" 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="375" height="59"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="59.5"/>
|
||||
<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="345" height="59"/>
|
||||
<rect key="frame" x="15" y="0.0" width="345" height="59.5"/>
|
||||
<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="custom" customColorSpace="sRGB"/>
|
||||
|
@ -1625,7 +1627,7 @@
|
|||
<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="375" height="269.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
|
|
|
@ -347,7 +347,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
|
|||
}];
|
||||
}
|
||||
|
||||
- (NSArray<Contact *> *)allContacts {
|
||||
- (NSArray<Contact *> *_Nonnull)allContacts {
|
||||
NSMutableArray *allContacts = [NSMutableArray array];
|
||||
|
||||
for (NSString *key in self.latestContactsById.allKeys) {
|
||||
|
|
|
@ -32,6 +32,7 @@ static NSString *const kCallSegue = @"2.0_6.0_Call_Segue";
|
|||
@class ContactsUpdater;
|
||||
@class TSNetworkManager;
|
||||
@class OWSMessageSender;
|
||||
@class UINavigationController;
|
||||
|
||||
@interface Environment : NSObject
|
||||
|
||||
|
|
|
@ -0,0 +1,379 @@
|
|||
// Originally based on EPContacts
|
||||
//
|
||||
// Created by Prabaharan Elangovan on 12/10/15.
|
||||
// Parts Copyright © 2015 Prabaharan Elangovan. All rights reserved.
|
||||
//
|
||||
// Modified for Signal by Michael Kirk on 11/25/2016
|
||||
// Parts Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import Contacts
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public protocol ContactsPickerDelegate {
|
||||
func contactsPicker(_: ContactsPicker, didContactFetchFailed error: NSError)
|
||||
func contactsPicker(_: ContactsPicker, didCancel error: NSError)
|
||||
func contactsPicker(_: ContactsPicker, didSelectContact contact: Contact)
|
||||
func contactsPicker(_: ContactsPicker, didSelectMultipleContacts contacts: [Contact])
|
||||
func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
public extension ContactsPickerDelegate {
|
||||
func contactsPicker(_: ContactsPicker, didContactFetchFailed error: NSError) { }
|
||||
func contactsPicker(_: ContactsPicker, didCancel error: NSError) { }
|
||||
func contactsPicker(_: ContactsPicker, didSelectContact contact: Contact) { }
|
||||
func contactsPicker(_: ContactsPicker, didSelectMultipleContacts contacts: [Contact]) { }
|
||||
func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool { return true }
|
||||
}
|
||||
|
||||
public enum SubtitleCellValue{
|
||||
case phoneNumber
|
||||
case email
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
open class ContactsPicker: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
let TAG = "[ContactsPicker]"
|
||||
let contactCellReuseIdentifier = "contactCellReuseIdentifier"
|
||||
let contactsManager: OWSContactsManager
|
||||
let collation = UILocalizedIndexedCollation.current()
|
||||
let contactStore = CNContactStore()
|
||||
lazy var resultSearchController = UISearchController()
|
||||
|
||||
// Data Source State
|
||||
lazy var sections = [[CNContact]]()
|
||||
lazy var filteredSections = [[CNContact]]()
|
||||
lazy var selectedContacts = [Contact]()
|
||||
|
||||
// Configuration
|
||||
open var contactsPickerDelegate: ContactsPickerDelegate?
|
||||
var subtitleCellValue = SubtitleCellValue.phoneNumber
|
||||
var multiSelectEnabled = false
|
||||
let allowedContactKeys: [CNKeyDescriptor] = [
|
||||
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
|
||||
CNContactThumbnailImageDataKey as CNKeyDescriptor,
|
||||
CNContactPhoneNumbersKey as CNKeyDescriptor,
|
||||
CNContactEmailAddressesKey as CNKeyDescriptor
|
||||
]
|
||||
|
||||
// MARK: - Lifecycle Methods
|
||||
|
||||
override open func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
title = NSLocalizedString("INVITE_FRIENDS_PICKER_TITLE", comment: "Navbar title")
|
||||
|
||||
// Don't obscure table header (search bar) with table index
|
||||
tableView.sectionIndexBackgroundColor = UIColor.clear
|
||||
|
||||
// Auto size cells for dynamic type
|
||||
tableView.estimatedRowHeight = 60.0
|
||||
tableView.rowHeight = UITableViewAutomaticDimension
|
||||
|
||||
tableView.allowsMultipleSelection = multiSelectEnabled
|
||||
|
||||
registerContactCell()
|
||||
initializeBarButtons()
|
||||
reloadContacts()
|
||||
initializeSearchBar()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.didChangePreferredContentSize), name: NSNotification.Name.UIContentSizeCategoryDidChange, object: nil)
|
||||
}
|
||||
|
||||
func didChangePreferredContentSize() {
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
func initializeSearchBar() {
|
||||
self.resultSearchController = ( {
|
||||
let controller = UISearchController(searchResultsController: nil)
|
||||
controller.searchResultsUpdater = self
|
||||
controller.dimsBackgroundDuringPresentation = false
|
||||
controller.searchBar.sizeToFit()
|
||||
controller.searchBar.delegate = self
|
||||
controller.hidesNavigationBarDuringPresentation = false
|
||||
|
||||
self.tableView.tableHeaderView = controller.searchBar
|
||||
return controller
|
||||
})()
|
||||
}
|
||||
|
||||
func initializeBarButtons() {
|
||||
let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.cancel, target: self, action: #selector(onTouchCancelButton))
|
||||
self.navigationItem.leftBarButtonItem = cancelButton
|
||||
|
||||
if multiSelectEnabled {
|
||||
let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(onTouchDoneButton))
|
||||
self.navigationItem.rightBarButtonItem = doneButton
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func registerContactCell() {
|
||||
tableView.register(ContactCell.nib, forCellReuseIdentifier: contactCellReuseIdentifier)
|
||||
}
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
override init(style: UITableViewStyle) {
|
||||
contactsManager = Environment.getCurrent().contactsManager
|
||||
super.init(style: style)
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
contactsManager = Environment.getCurrent().contactsManager
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
convenience public init(delegate: ContactsPickerDelegate?) {
|
||||
self.init(delegate: delegate, multiSelection: false)
|
||||
}
|
||||
|
||||
convenience public init(delegate: ContactsPickerDelegate?, multiSelection : Bool) {
|
||||
self.init(style: .plain)
|
||||
multiSelectEnabled = multiSelection
|
||||
contactsPickerDelegate = delegate
|
||||
}
|
||||
|
||||
convenience public init(delegate: ContactsPickerDelegate?, multiSelection : Bool, subtitleCellType: SubtitleCellValue) {
|
||||
self.init(style: .plain)
|
||||
multiSelectEnabled = multiSelection
|
||||
contactsPickerDelegate = delegate
|
||||
subtitleCellValue = subtitleCellType
|
||||
}
|
||||
|
||||
// MARK: - Contact Operations
|
||||
|
||||
open func reloadContacts() {
|
||||
getContacts( onError: { error in
|
||||
Logger.error("\(self.TAG) failed to reload contacts with error:\(error)")
|
||||
})
|
||||
}
|
||||
|
||||
func getContacts(onError errorHandler: @escaping (_ error: Error) -> Void) {
|
||||
switch CNContactStore.authorizationStatus(for: CNEntityType.contacts) {
|
||||
case CNAuthorizationStatus.denied, CNAuthorizationStatus.restricted:
|
||||
|
||||
let title = NSLocalizedString("AB_PERMISSION_MISSING_TITLE", comment: "Alert title when contacts disabled")
|
||||
let body = NSLocalizedString("ADDRESSBOOK_RESTRICTED_ALERT_BODY", comment: "Alert body when contacts disabled")
|
||||
let alert = UIAlertController(title: title, message: body, preferredStyle: UIAlertControllerStyle.alert)
|
||||
|
||||
let dismissText = NSLocalizedString("DISMISS_BUTTON_TEXT", comment:"")
|
||||
|
||||
let okAction = UIAlertAction(title: dismissText, style: UIAlertActionStyle.default, handler: { action in
|
||||
let error = NSError(domain: "contactsPickerErrorDomain", code: 1, userInfo: [NSLocalizedDescriptionKey: "No Contacts Access"])
|
||||
self.contactsPickerDelegate?.contactsPicker(self, didContactFetchFailed: error)
|
||||
errorHandler(error)
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
})
|
||||
alert.addAction(okAction)
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
|
||||
case CNAuthorizationStatus.notDetermined:
|
||||
//This case means the user is prompted for the first time for allowing contacts
|
||||
contactStore.requestAccess(for: CNEntityType.contacts) { (granted, error) -> Void in
|
||||
//At this point an alert is provided to the user to provide access to contacts. This will get invoked if a user responds to the alert
|
||||
if granted {
|
||||
self.getContacts(onError: errorHandler)
|
||||
} else {
|
||||
errorHandler(error!)
|
||||
}
|
||||
}
|
||||
|
||||
case CNAuthorizationStatus.authorized:
|
||||
//Authorization granted by user for this app.
|
||||
var contacts = [CNContact]()
|
||||
|
||||
do {
|
||||
let contactFetchRequest = CNContactFetchRequest(keysToFetch: allowedContactKeys)
|
||||
try contactStore.enumerateContacts(with: contactFetchRequest) { (contact, stop) -> Void in
|
||||
contacts.append(contact)
|
||||
}
|
||||
self.sections = collatedContacts(contacts)
|
||||
self.tableView.reloadData()
|
||||
} catch let error as NSError {
|
||||
Logger.error("\(self.TAG) Failed to fetch contacts with error:\(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collatedContacts(_ contacts: [CNContact]) -> [[CNContact]] {
|
||||
let selector: Selector = #selector(getter: CNContact.nameForCollating)
|
||||
|
||||
var collated = Array(repeating: [CNContact](), count: collation.sectionTitles.count)
|
||||
for contact in contacts {
|
||||
let sectionNumber = collation.section(for: contact, collationStringSelector: selector)
|
||||
collated[sectionNumber].append(contact)
|
||||
}
|
||||
return collated
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Table View DataSource
|
||||
|
||||
override open func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return self.collation.sectionTitles.count
|
||||
}
|
||||
|
||||
override open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
let dataSource = resultSearchController.isActive ? filteredSections : sections
|
||||
|
||||
return dataSource[section].count
|
||||
}
|
||||
|
||||
// MARK: - Table View Delegates
|
||||
|
||||
override open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: contactCellReuseIdentifier, for: indexPath) as! ContactCell
|
||||
|
||||
let dataSource = resultSearchController.isActive ? filteredSections : sections
|
||||
let cnContact = dataSource[indexPath.section][indexPath.row]
|
||||
let contact = Contact(contact: cnContact)
|
||||
|
||||
cell.updateContactsinUI(contact, subtitleType: subtitleCellValue, contactsManager: self.contactsManager)
|
||||
let isSelected = selectedContacts.contains(where: { $0.uniqueId == contact.uniqueId })
|
||||
cell.isSelected = isSelected
|
||||
|
||||
// Make sure we preserve selection across tableView.reloadData which happens when toggling between
|
||||
// search controller
|
||||
if (isSelected) {
|
||||
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
||||
} else {
|
||||
self.tableView.deselectRow(at: indexPath, animated: false)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override open func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
|
||||
let cell = tableView.cellForRow(at: indexPath) as! ContactCell
|
||||
let deselectedContact = cell.contact!
|
||||
|
||||
selectedContacts = selectedContacts.filter() {
|
||||
return $0.uniqueId != deselectedContact.uniqueId
|
||||
}
|
||||
}
|
||||
|
||||
override open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let cell = tableView.cellForRow(at: indexPath) as! ContactCell
|
||||
let selectedContact = cell.contact!
|
||||
|
||||
guard (contactsPickerDelegate == nil || contactsPickerDelegate!.contactsPicker(self, shouldSelectContact: selectedContact)) else {
|
||||
self.tableView.deselectRow(at: indexPath, animated: false)
|
||||
return
|
||||
}
|
||||
|
||||
selectedContacts.append(selectedContact)
|
||||
|
||||
if !multiSelectEnabled {
|
||||
//Single selection code
|
||||
resultSearchController.isActive = false
|
||||
self.dismiss(animated: true) {
|
||||
self.contactsPickerDelegate?.contactsPicker(self, didSelectContact: selectedContact)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
||||
return collation.section(forSectionIndexTitle: index)
|
||||
}
|
||||
|
||||
override open func sectionIndexTitles(for tableView: UITableView) -> [String]? {
|
||||
return collation.sectionIndexTitles
|
||||
}
|
||||
|
||||
override open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
let dataSource = resultSearchController.isActive ? filteredSections : sections
|
||||
|
||||
if dataSource[section].count > 0 {
|
||||
return collation.sectionTitles[section]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Button Actions
|
||||
|
||||
func onTouchCancelButton() {
|
||||
contactsPickerDelegate?.contactsPicker(self, didCancel: NSError(domain: "contactsPickerErrorDomain", code: 2, userInfo: [ NSLocalizedDescriptionKey: "User Canceled Selection"]))
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func onTouchDoneButton() {
|
||||
contactsPickerDelegate?.contactsPicker(self, didSelectMultipleContacts: selectedContacts)
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: - Search Actions
|
||||
|
||||
open func updateSearchResults(for searchController: UISearchController) {
|
||||
if let searchText = resultSearchController.searchBar.text , searchController.isActive {
|
||||
|
||||
let predicate: NSPredicate
|
||||
if searchText.characters.count == 0 {
|
||||
filteredSections = sections
|
||||
} else {
|
||||
do {
|
||||
predicate = CNContact.predicateForContacts(matchingName: searchText)
|
||||
let filteredContacts = try contactStore.unifiedContacts(matching: predicate, keysToFetch: allowedContactKeys)
|
||||
filteredSections = collatedContacts(filteredContacts)
|
||||
} catch let error as NSError {
|
||||
Logger.error("\(self.TAG) updating search results failed with error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
open func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
let ContactSortOrder = computeSortOrder()
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func computeSortOrder() -> CNContactSortOrder {
|
||||
let comparator = CNContact.comparator(forNameSortOrder: .userDefault)
|
||||
|
||||
let contact0 = CNMutableContact()
|
||||
contact0.givenName = "A"
|
||||
contact0.familyName = "Z"
|
||||
|
||||
let contact1 = CNMutableContact()
|
||||
contact1.givenName = "Z"
|
||||
contact1.familyName = "A"
|
||||
|
||||
let result = comparator(contact0, contact1)
|
||||
|
||||
if result == .orderedAscending {
|
||||
return .givenName
|
||||
} else {
|
||||
return .familyName
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
fileprivate extension CNContact {
|
||||
/**
|
||||
* Sorting Key used by collation
|
||||
*/
|
||||
@objc var nameForCollating: String {
|
||||
get {
|
||||
let compositeName: String
|
||||
if ContactSortOrder == .familyName {
|
||||
compositeName = "\(self.familyName) \(self.givenName)"
|
||||
} else {
|
||||
compositeName = "\(self.givenName) \(self.familyName)"
|
||||
}
|
||||
return compositeName.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,8 +7,7 @@ import ContactsUI
|
|||
import MessageUI
|
||||
|
||||
@objc(OWSInviteFlow)
|
||||
class InviteFlow: NSObject, CNContactPickerDelegate, MFMessageComposeViewControllerDelegate, MFMailComposeViewControllerDelegate {
|
||||
|
||||
class InviteFlow: NSObject, MFMessageComposeViewControllerDelegate, MFMailComposeViewControllerDelegate, ContactsPickerDelegate {
|
||||
enum Channel {
|
||||
case message, mail, twitter
|
||||
}
|
||||
|
@ -20,11 +19,13 @@ class InviteFlow: NSObject, CNContactPickerDelegate, MFMessageComposeViewControl
|
|||
|
||||
let actionSheetController: UIAlertController
|
||||
let presentingViewController: UIViewController
|
||||
let contactsManager: OWSContactsManager
|
||||
|
||||
var channel: Channel?
|
||||
|
||||
required init(presentingViewController: UIViewController) {
|
||||
required init(presentingViewController: UIViewController, contactsManager: OWSContactsManager) {
|
||||
self.presentingViewController = presentingViewController
|
||||
|
||||
self.contactsManager = contactsManager
|
||||
actionSheetController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
super.init()
|
||||
|
@ -68,7 +69,7 @@ class InviteFlow: NSObject, CNContactPickerDelegate, MFMessageComposeViewControl
|
|||
|
||||
let tweetUrl = URL(string: installUrl)
|
||||
twitterViewController.add(tweetUrl)
|
||||
twitterViewController.add(#imageLiteral(resourceName: "logo_with_background"))
|
||||
twitterViewController.add(#imageLiteral(resourceName: "twitter_sharing_image"))
|
||||
|
||||
let tweetTitle = NSLocalizedString("SHARE_ACTION_TWEET", comment:"action sheet item")
|
||||
return UIAlertAction(title: tweetTitle, style: .default) { action in
|
||||
|
@ -82,24 +83,10 @@ class InviteFlow: NSObject, CNContactPickerDelegate, MFMessageComposeViewControl
|
|||
return UIAlertAction(title: NSLocalizedString("DISMISS_BUTTON_TEXT", comment:""), style: .cancel)
|
||||
}
|
||||
|
||||
// MARK: ContactPickerDelegate
|
||||
// MARK: ContactsPickerDelegate
|
||||
|
||||
/*!
|
||||
* @abstract Invoked when the picker is closed.
|
||||
* @discussion The picker will be dismissed automatically after a contact or property is picked.
|
||||
*/
|
||||
@available(iOS 9.0, *)
|
||||
func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
|
||||
Logger.debug("\(TAG) pickerDidCancel")
|
||||
}
|
||||
|
||||
/*!
|
||||
* @abstract Plural delegate methods.
|
||||
* @discussion These delegate methods will be invoked when the user is done selecting multiple contacts or properties.
|
||||
* Implementing one of these methods will configure the picker for multi-selection.
|
||||
*/
|
||||
@available(iOS 9.0, *)
|
||||
func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
|
||||
func contactsPicker(_: ContactsPicker, didSelectMultipleContacts contacts: [Contact]) {
|
||||
Logger.debug("\(TAG) didSelectContacts:\(contacts)")
|
||||
|
||||
guard let inviteChannel = channel else {
|
||||
|
@ -109,24 +96,38 @@ class InviteFlow: NSObject, CNContactPickerDelegate, MFMessageComposeViewControl
|
|||
|
||||
switch inviteChannel {
|
||||
case .message:
|
||||
sendSMSTo(contacts: contacts)
|
||||
let phoneNumbers: [String] = contacts.map { $0.userTextPhoneNumbers.first }.filter { $0 != nil }.map { $0! }
|
||||
sendSMSTo(phoneNumbers: phoneNumbers)
|
||||
case .mail:
|
||||
sendMailTo(contacts:contacts)
|
||||
let recipients: [String] = contacts.map { $0.emails.first }.filter { $0 != nil }.map { $0! }
|
||||
sendMailTo(emails: recipients)
|
||||
default:
|
||||
Logger.error("\(TAG) unexpected channel after returning from contact picker: \(inviteChannel)")
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func contactPicker(_ picker: CNContactPickerViewController, didSelectContactProperties contactProperties: [CNContactProperty]) {
|
||||
Logger.debug("\(TAG) didSelectContactProperties:\(contactProperties)")
|
||||
func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool {
|
||||
guard let inviteChannel = channel else {
|
||||
Logger.error("\(TAG) unexpected nil channel in contact picker.")
|
||||
return true
|
||||
}
|
||||
|
||||
switch inviteChannel {
|
||||
case .message:
|
||||
return contact.userTextPhoneNumbers.count > 0
|
||||
case .mail:
|
||||
return contact.emails.count > 0
|
||||
default:
|
||||
Logger.error("\(TAG) unexpected channel after returning from contact picker: \(inviteChannel)")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: SMS
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func messageAction() -> UIAlertAction? {
|
||||
|
||||
guard MFMessageComposeViewController.canSendText() else {
|
||||
Logger.info("\(TAG) Device cannot send text")
|
||||
return nil
|
||||
|
@ -136,18 +137,13 @@ class InviteFlow: NSObject, CNContactPickerDelegate, MFMessageComposeViewControl
|
|||
return UIAlertAction(title: messageTitle, style: .default) { action in
|
||||
Logger.debug("\(self.TAG) Chose message.")
|
||||
self.channel = .message
|
||||
|
||||
let picker = CNContactPickerViewController()
|
||||
picker.predicateForSelectionOfContact = NSPredicate(value: false)
|
||||
picker.predicateForEnablingContact = NSPredicate(format: "phoneNumbers.@count > 0")
|
||||
picker.delegate = self
|
||||
|
||||
self.presentingViewController.present(picker, animated: true, completion: nil)
|
||||
let picker = ContactsPicker(delegate: self, multiSelection: true, subtitleCellType: .phoneNumber)
|
||||
let navigationController = UINavigationController(rootViewController: picker)
|
||||
self.presentingViewController.present(navigationController, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func sendSMSTo(contacts: [CNContact]) {
|
||||
func sendSMSTo(phoneNumbers: [String]) {
|
||||
self.presentingViewController.dismiss(animated: true) {
|
||||
if #available(iOS 10.0, *) {
|
||||
// iOS10 message compose view doesn't respect some system appearence attributes.
|
||||
|
@ -159,7 +155,7 @@ class InviteFlow: NSObject, CNContactPickerDelegate, MFMessageComposeViewControl
|
|||
}
|
||||
let messageComposeViewController = MFMessageComposeViewController()
|
||||
messageComposeViewController.messageComposeDelegate = self
|
||||
messageComposeViewController.recipients = contacts.map { $0.phoneNumbers.first }.filter { $0 != nil }.map { $0!.value.stringValue }
|
||||
messageComposeViewController.recipients = phoneNumbers
|
||||
|
||||
let inviteText = NSLocalizedString("SMS_INVITE_BODY", comment:"body sent to contacts when inviting to Install Signal")
|
||||
messageComposeViewController.body = inviteText.appending(" \(self.installUrl)")
|
||||
|
@ -200,22 +196,18 @@ class InviteFlow: NSObject, CNContactPickerDelegate, MFMessageComposeViewControl
|
|||
Logger.debug("\(self.TAG) Chose mail.")
|
||||
self.channel = .mail
|
||||
|
||||
let picker = CNContactPickerViewController()
|
||||
picker.predicateForSelectionOfContact = NSPredicate(value: false)
|
||||
picker.predicateForEnablingContact = NSPredicate(format: "emailAddresses.@count > 0")
|
||||
picker.delegate = self
|
||||
|
||||
self.presentingViewController.present(picker, animated: true)
|
||||
let picker = ContactsPicker(delegate: self, multiSelection: true, subtitleCellType: .email)
|
||||
let navigationController = UINavigationController(rootViewController: picker)
|
||||
self.presentingViewController.present(navigationController, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
func sendMailTo(contacts: [CNContact]) {
|
||||
func sendMailTo(emails recipientEmails: [String]) {
|
||||
let mailComposeViewController = MFMailComposeViewController()
|
||||
mailComposeViewController.mailComposeDelegate = self
|
||||
|
||||
let recipients: [String] = contacts.map { $0.emailAddresses.first }.filter { $0 != nil }.map { $0!.value as String }
|
||||
mailComposeViewController.setBccRecipients(recipients)
|
||||
mailComposeViewController.setBccRecipients(recipientEmails)
|
||||
|
||||
let subject = NSLocalizedString("EMAIL_INVITE_SUBJECT", comment:"subject of email sent to contacts when inviting to install Signal")
|
||||
let bodyFormat = NSLocalizedString("EMAIL_INVITE_BODY", comment:"body of email sent to contacts when inviting to install Signal. Embeds {{link to install Signal}} and {{link to WhisperSystems home page}}")
|
||||
|
|
|
@ -75,6 +75,9 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
|
|||
[super viewDidLoad];
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
|
||||
self.tableView.estimatedRowHeight = (CGFloat)60.0;
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
|
||||
self.contacts = self.contactsManager.signalContacts;
|
||||
self.searchResults = self.contacts;
|
||||
[self initializeSearch];
|
||||
|
@ -430,6 +433,10 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
|
|||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
if (section == MessageComposeTableViewControllerSectionInvite) {
|
||||
if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_9_0) {
|
||||
// Invite flow not supported on iOS8
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
if (self.searchController.active) {
|
||||
|
@ -454,17 +461,14 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie
|
|||
}
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 52.0f;
|
||||
}
|
||||
|
||||
#pragma mark - Table View delegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
if (indexPath.section == MessageComposeTableViewControllerSectionInvite) {
|
||||
void (^showInvite)() = ^{
|
||||
OWSInviteFlow *inviteFlow = [[OWSInviteFlow alloc] initWithPresentingViewController:self];
|
||||
OWSInviteFlow *inviteFlow =
|
||||
[[OWSInviteFlow alloc] initWithPresentingViewController:self contactsManager:self.contactsManager];
|
||||
[self presentViewController:inviteFlow.actionSheetController
|
||||
animated:YES
|
||||
completion:^{
|
||||
|
|
|
@ -57,10 +57,36 @@ typedef enum {
|
|||
|
||||
@interface SettingsTableViewController () <UIAlertViewDelegate>
|
||||
|
||||
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SettingsTableViewController
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_contactsManager = [Environment getCurrent].contactsManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_contactsManager = [Environment getCurrent].contactsManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
@ -133,7 +159,9 @@ typedef enum {
|
|||
case kGeneralSection: {
|
||||
switch (indexPath.row) {
|
||||
case kInviteRow: {
|
||||
OWSInviteFlow *inviteFlow = [[OWSInviteFlow alloc] initWithPresentingViewController:self];
|
||||
OWSInviteFlow *inviteFlow =
|
||||
[[OWSInviteFlow alloc] initWithPresentingViewController:self
|
||||
contactsManager:self.contactsManager];
|
||||
[self presentViewController:inviteFlow.actionSheetController
|
||||
animated:YES
|
||||
completion:^{
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
// Originally based on EPContacts
|
||||
//
|
||||
// Created by Prabaharan Elangovan on 13/10/15.
|
||||
// Copyright © 2015 Prabaharan Elangovan. All rights reserved.
|
||||
//
|
||||
// Modified for Signal by Michael Kirk on 11/25/2016
|
||||
// Parts Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
class ContactCell: UITableViewCell {
|
||||
|
||||
static let nib = UINib(nibName:"ContactCell", bundle: nil)
|
||||
|
||||
@IBOutlet weak var contactTextLabel: UILabel!
|
||||
@IBOutlet weak var contactDetailTextLabel: UILabel!
|
||||
@IBOutlet weak var contactImageView: UIImageView!
|
||||
@IBOutlet weak var contactContainerView: UIView!
|
||||
|
||||
var contact: Contact?
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
// Initialization code
|
||||
selectionStyle = UITableViewCellSelectionStyle.none
|
||||
|
||||
contactContainerView.layer.masksToBounds = true
|
||||
contactContainerView.layer.cornerRadius = contactContainerView.frame.size.width/2
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.didChangePreferredContentSize), name: NSNotification.Name.UIContentSizeCategoryDidChange, object: nil)
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
accessoryType = .none
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
accessoryType = selected ? .checkmark : .none
|
||||
}
|
||||
|
||||
func didChangePreferredContentSize() {
|
||||
contactTextLabel.font = UIFont.preferredFont(forTextStyle: .body)
|
||||
contactDetailTextLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
|
||||
}
|
||||
|
||||
func updateContactsinUI(_ contact: Contact, subtitleType: SubtitleCellValue, contactsManager: OWSContactsManager) {
|
||||
self.contact = contact
|
||||
|
||||
if contactTextLabel != nil {
|
||||
contactTextLabel.attributedText = contact.cnContact?.formattedFullName(font:contactTextLabel.font)
|
||||
}
|
||||
|
||||
updateSubtitleBasedonType(subtitleType, contact: contact)
|
||||
|
||||
if contact.image == nil {
|
||||
let contactIdForDeterminingBackgroundColor: String
|
||||
if let signalId = contact.parsedPhoneNumbers.first?.toE164() {
|
||||
contactIdForDeterminingBackgroundColor = signalId
|
||||
} else {
|
||||
contactIdForDeterminingBackgroundColor = contact.fullName
|
||||
}
|
||||
|
||||
let avatarBuilder = OWSContactAvatarBuilder(contactId:contactIdForDeterminingBackgroundColor,
|
||||
name:contact.fullName,
|
||||
contactsManager:contactsManager)
|
||||
self.contactImageView?.image = avatarBuilder.buildDefaultImage();
|
||||
} else {
|
||||
self.contactImageView?.image = contact.image
|
||||
}
|
||||
}
|
||||
|
||||
func updateSubtitleBasedonType(_ subtitleType: SubtitleCellValue , contact: Contact) {
|
||||
switch subtitleType {
|
||||
|
||||
case SubtitleCellValue.phoneNumber:
|
||||
if contact.userTextPhoneNumbers.count > 0 {
|
||||
self.contactDetailTextLabel.text = "\(contact.userTextPhoneNumbers[0])"
|
||||
} else {
|
||||
self.contactDetailTextLabel.text = NSLocalizedString("CONTACT_PICKER_NO_PHONE_NUMBERS_AVAILABLE", comment: "table cell subtitle when contact card has no known phone number")
|
||||
}
|
||||
case SubtitleCellValue.email:
|
||||
if contact.emails.count > 0 {
|
||||
self.contactDetailTextLabel.text = "\(contact.emails[0])"
|
||||
} else {
|
||||
self.contactDetailTextLabel.text = NSLocalizedString("CONTACT_PICKER_NO_EMAILS_AVAILABLE", comment: "table cell subtitle when contact card has no email")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, *)
|
||||
fileprivate extension CNContact {
|
||||
/**
|
||||
* Bold the sorting portion of the name. e.g. if we sort by family name, bold the family name.
|
||||
*/
|
||||
func formattedFullName(font: UIFont) -> NSAttributedString? {
|
||||
let keyToHighlight = ContactSortOrder == .familyName ? CNContactFamilyNameKey : CNContactGivenNameKey
|
||||
|
||||
if let attributedName = CNContactFormatter.attributedString(from: self, style: .fullName, defaultAttributes: nil) {
|
||||
let highlightedName = attributedName.mutableCopy() as! NSMutableAttributedString
|
||||
highlightedName.enumerateAttributes(in: NSMakeRange(0, highlightedName.length), options: [], using: { (attrs, range, stop) in
|
||||
if let property = attrs[CNContactPropertyAttribute] as? String, property == keyToHighlight {
|
||||
let boldDescriptor = font.fontDescriptor.withSymbolicTraits(.traitBold)
|
||||
let boldAttributes = [
|
||||
NSFontAttributeName: UIFont(descriptor:boldDescriptor!, size: 0)
|
||||
]
|
||||
|
||||
highlightedName.addAttributes(boldAttributes, range: range)
|
||||
}
|
||||
})
|
||||
return highlightedName
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11542" systemVersion="15G1108" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="60" id="KGk-i7-Jjw" customClass="ContactCell" customModule="Signal" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="59"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bKn-WF-9hk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="59"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="59" id="z02-WI-o4t"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HUe-8f-NjY">
|
||||
<rect key="frame" x="60" y="10" width="250" height="21"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Tqx-Gt-ofy">
|
||||
<rect key="frame" x="60" y="35" width="234" height="18"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Bvt-tZ-NrJ">
|
||||
<rect key="frame" x="10" y="10" width="40" height="40"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mxT-DB-6lI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="40" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="0.7725490196" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="40" id="8v6-UT-2rU"/>
|
||||
<constraint firstAttribute="height" constant="40" id="pAq-lQ-W6t"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Bvt-tZ-NrJ" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="1V9-eI-crl"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Tqx-Gt-ofy" secondAttribute="trailing" constant="26" id="36W-0D-Qv3"/>
|
||||
<constraint firstItem="HUe-8f-NjY" firstAttribute="leading" secondItem="Bvt-tZ-NrJ" secondAttribute="trailing" constant="10" id="8Ws-h4-oJp"/>
|
||||
<constraint firstItem="bKn-WF-9hk" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="NDc-ZH-7qQ"/>
|
||||
<constraint firstItem="Tqx-Gt-ofy" firstAttribute="leading" secondItem="Bvt-tZ-NrJ" secondAttribute="trailing" constant="10" id="PDp-yF-F2b"/>
|
||||
<constraint firstItem="HUe-8f-NjY" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="R7t-dd-hcs"/>
|
||||
<constraint firstItem="Tqx-Gt-ofy" firstAttribute="top" secondItem="HUe-8f-NjY" secondAttribute="bottom" constant="4" id="aNx-bp-Uj5"/>
|
||||
<constraint firstAttribute="trailing" secondItem="HUe-8f-NjY" secondAttribute="trailing" constant="10" id="cOY-df-aFi"/>
|
||||
<constraint firstItem="Bvt-tZ-NrJ" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="10" id="m7q-1p-kjc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Tqx-Gt-ofy" secondAttribute="bottom" constant="6" id="mEe-il-eMD"/>
|
||||
<constraint firstAttribute="bottom" secondItem="bKn-WF-9hk" secondAttribute="bottom" id="oHg-Fe-wLe"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bKn-WF-9hk" secondAttribute="trailing" id="p0G-Qg-21x"/>
|
||||
<constraint firstItem="bKn-WF-9hk" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="q4R-qf-Bvi"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="contactContainerView" destination="Bvt-tZ-NrJ" id="qqg-f5-Xol"/>
|
||||
<outlet property="contactDetailTextLabel" destination="Tqx-Gt-ofy" id="Jlj-TK-UJl"/>
|
||||
<outlet property="contactImageView" destination="mxT-DB-6lI" id="aYl-FS-HAU"/>
|
||||
<outlet property="contactTextLabel" destination="HUe-8f-NjY" id="Smr-wZ-MHr"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="245" y="321"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
TARGETS="Signal/src ../SignalServiceKit/src Pods/JSQMessagesViewController"
|
||||
TARGETS="Signal/src Libraries/EPContactsPicker ../SignalServiceKit/src Pods/JSQMessagesViewController"
|
||||
TMP="$(mktemp -d)"
|
||||
STRINGFILE="Signal/translations/en.lproj/Localizable.strings"
|
||||
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue