123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // Threema iOS Client
- // Copyright (c) 2019-2020 Threema GmbH
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License, version 3,
- // as published by the Free Software Foundation.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- import UIKit
- import ThreemaFramework
- import QuartzCore
- import Contacts
- import ContactsUI
- @objc protocol ContactDetailsViewControllerDelegate: class {
- @objc func present(contactDetailsViewController: ContactDetailsViewController, onCompletion: @escaping ((_ contactsDetailsViewController: ContactDetailsViewController) -> Void))
- }
- class ContactDetailsViewController: ThemedTableViewController {
-
- @IBOutlet weak var headerView: UIView!
- @IBOutlet weak var disclosureButton: UIButton!
- @IBOutlet weak var imageView: UIImageView!
- @IBOutlet weak var nameLabel: UILabel!
-
- @IBOutlet weak var companyNameLabel: UILabel!
- @IBOutlet weak var threemaTypeIcon: UIButton!
- @IBOutlet weak var scanQrCodeBarButtonItem: UIBarButtonItem!
-
- @objc var contact: Contact?
- @objc var hideActionButtons: Bool = false
- @objc weak var delegate : ContactDetailsViewControllerDelegate?
-
- private var didHideTabBar: Bool = false
- private var callNumbers: [String]?
- private var cnAddressBook: CNContactStore = CNContactStore()
- private var cnContact: CNContact?
- private var cnContactViewShowing: Bool = false
- private var canExportConversation: Bool = false
- private var conversation: Conversation?
-
- private var showcase: MaterialShowcase?
-
- private var kvoContact: NSKeyValueObservation?
-
- private let THREEMA_ID_SHARE_LINK = "https://threema.id/"
-
- override internal var shouldAutorotate : Bool {
- return true
- }
-
- override internal var supportedInterfaceOrientations: UIInterfaceOrientationMask {
- if UIDevice.current.userInterfaceIdiom == .pad {
- return UIInterfaceOrientationMask.all
- }
- return UIInterfaceOrientationMask.allButUpsideDown
- }
-
- override internal var previewActionItems: [UIPreviewActionItem] {
- let sendMessageAction = UIPreviewAction.init(title: BundleUtil.localizedString(forKey: "send_message"), style: .default) { (action, previewController) in
- self.sendMessageAction()
- }
- let scanQrCodeAction = UIPreviewAction.init(title: BundleUtil.localizedString(forKey: "scan_qr"), style: .default) { (action, previewController) in
- // we need to present contact details first and present qr scanner on top of that
- self.delegate?.present(contactDetailsViewController: self, onCompletion: { (contactsDetailsViewController) in
- contactsDetailsViewController.scanIdentityAction()
- })
- }
-
- return [sendMessageAction, scanQrCodeAction]
- }
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- if ScanIdentityController.canScan() == false {
- navigationItem.rightBarButtonItem = nil
- }
-
- navigationController?.interactivePopGestureRecognizer?.isEnabled = true
- navigationController?.interactivePopGestureRecognizer?.delegate = nil
-
- NotificationCenter.default.addObserver(forName: Notification.Name(kNotificationColorThemeChanged), object: nil, queue: nil) { (notification) in
- self.setupColors()
- }
- NotificationCenter.default.addObserver(forName: Notification.Name(kNotificationShowProfilePictureChanged), object: nil, queue: nil) { (notification) in
- self.updateView()
- }
-
- let disclosureTapRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(tappedHeaderView))
- disclosureButton.addGestureRecognizer(disclosureTapRecognizer)
- disclosureButton.accessibilityLabel = BundleUtil.localizedString(forKey: "edit_contact")
-
- let tapRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(tappedImage))
- imageView.addGestureRecognizer(tapRecognizer)
-
- threemaTypeIcon.setTitle("", for: .normal)
- threemaTypeIcon.setBackgroundImage(Utils.threemaTypeIcon(), for: .normal)
-
- setupColors()
- }
-
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
-
- if cnContactViewShowing {
- cnContactViewShowing = false
- ContactStore.shared()?.update(contact)
- let statusNavigationBar = navigationController?.navigationBar as! StatusNavigationBar
- statusNavigationBar.showOrHideStatusView()
- Colors.update(navigationController?.navigationBar)
- }
-
- view.alpha = 1.0
- updateView()
- }
-
- override func viewDidAppear(_ animated: Bool) {
- super.viewDidAppear(animated)
-
- if navigationController != nil {
- if navigationController!.isNavigationBarHidden {
- navigationController?.isNavigationBarHidden = false
- }
- }
-
- if UserSettings.shared().workInfoShown == false && !Utils.hideThreemaTypeIcon(for: contact) {
- showWorkInfo()
- }
-
- if #available(iOS 13.0, *) {
- kvoContact = contact!.observe(\.verificationLevel, options: .new) { (changedContact, change) in
- DispatchQueue.main.async {
- self.updateView()
- }
- }
- }
- }
-
- override func viewWillDisappear(_ animated: Bool) {
- super.viewWillDisappear(animated)
-
- if #available(iOS 13.0, *) {
- kvoContact?.invalidate()
- }
- }
-
- override func viewWillLayoutSubviews() {
- super.viewWillLayoutSubviews()
-
- let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .title3)
- nameLabel.font = UIFont.boldSystemFont(ofSize: fontDescriptor.pointSize)
- }
-
- override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
- if segue.identifier == "EditName" {
- let editVC = segue.destination as? EditContactViewController
- editVC?.contact = contact
- }
- else if segue.identifier == "ShowPushSetting" {
- let notificationSettingViewController = segue.destination as? NotificationSettingViewController
- notificationSettingViewController?.identity = contact?.identity
- notificationSettingViewController?.isGroup = false
- notificationSettingViewController?.conversation = conversation
- }
- }
- }
- extension ContactDetailsViewController {
- // MARK: public functions
-
- @objc public func sendMessageAction() {
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
- let info: [AnyHashable: Any] = [kKeyContact : contact!, kKeyForceCompose: NSNumber.init(value: true)]
- NotificationCenter.default.post(name: NSNotification.Name(rawValue: kNotificationShowConversation), object: nil, userInfo: info)
- }
-
- @objc public func scanIdentityAction() {
- let scanController = ScanIdentityController.init()
- scanController.containingViewController = self
- scanController.expectedIdentity = contact?.identity
- scanController.popupScanResults = false
- scanController.startScan()
- }
-
- @objc public func startProfilePictureAction() {
- let sender = ContactPhotoSender.init()
- sender.startWithImage(toMember: contact, onCompletion: {
- UIAlertTemplate .showAlert(owner: self, title: BundleUtil.localizedString(forKey: "my_profilepicture"), message: BundleUtil.localizedString(forKey: "contact_send_profilepicture_success"))
- }) { (error) in
- UIAlertTemplate .showAlert(owner: self, title: BundleUtil.localizedString(forKey: "my_profilepicture"), message: BundleUtil.localizedString(forKey: "contact_send_profilepicture_error"))
- }
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
- }
-
- @objc public func startThreemaCallAction(_ startWithVideo: Bool = false) {
- if VoIPCallStateManager.shared.currentCallState() == .idle {
- var contactSet = Set<Contact>()
- contactSet.insert(contact!)
-
- FeatureMask.check(Int(FEATURE_MASK_VOIP), forContacts: contactSet) { (unsupportedContacts) in
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
- if unsupportedContacts == nil {
- UIAlertTemplate.showAlert(owner: self, title: BundleUtil.localizedString(forKey: "call_voip_not_supported_title"), message: BundleUtil.localizedString(forKey: "call_voip_not_supported_text"))
- return
- }
-
- if unsupportedContacts!.count == 0 {
- self.startVoipCall(startWithVideo)
- } else {
- UIAlertTemplate.showAlert(owner: self, title: BundleUtil.localizedString(forKey: "call_voip_not_supported_title"), message: BundleUtil.localizedString(forKey: "call_voip_not_supported_text"))
- }
- }
- } else {
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
- }
- }
- }
- extension ContactDetailsViewController {
- // MARK: private functions
-
- private func setupColors() {
- nameLabel.textColor = Colors.fontNormal()
- nameLabel.shadowColor = nil
-
- companyNameLabel.textColor = Colors.fontNormal()
- companyNameLabel.shadowColor = nil
-
- let disclosureImage: UIImage
- if #available(iOS 13.0, *) {
- disclosureImage = disclosureButton.imageView!.image!.withTintColor(Colors.main())
- } else {
- disclosureImage = disclosureButton.imageView!.image!.withTint(Colors.main())
- }
- disclosureButton.setImage(disclosureImage, for: .normal)
-
- if #available(iOS 11.0, *) {
- imageView.accessibilityIgnoresInvertColors = true
- threemaTypeIcon.accessibilityIgnoresInvertColors = true
- }
- }
-
- private func updateView() {
- if scanQrCodeBarButtonItem != nil {
- scanQrCodeBarButtonItem.accessibilityLabel = BundleUtil.localizedString(forKey: "scan_identity")
- }
-
- navigationItem.title = contact?.displayName
- nameLabel.text = contact?.displayName
- headerView.accessibilityLabel = contact?.displayName
-
- imageView.image = AvatarMaker.shared()?.avatar(for: contact, size: imageView.frame.size.width, masked: false)
- imageView.contentMode = .scaleAspectFill
- imageView.layer.masksToBounds = true
- imageView.layer.cornerRadius = imageView.bounds.size.width / 2
-
- threemaTypeIcon.isHidden = Utils.hideThreemaTypeIcon(for: contact)
-
- companyNameLabel.text = ""
- cnContact = nil
-
- if contact?.cnContactId != nil {
- updateViewWithCNContact()
- }
-
- companyNameLabel.isHidden = companyNameLabel.text?.count == 0
- let headerHeight: CGFloat = companyNameLabel.text?.count == 0 ? 275.0 : 300.0
- headerView.frame = CGRect.init(x: headerView.frame.origin.x, y: headerView.frame.origin.y, width: headerView.frame.size.width, height: headerHeight)
-
- isExportConversationEnabled()
-
- if didHideTabBar {
- tabBarController?.tabBar.isHidden = false
- didHideTabBar = false
- }
- tableView.reloadData()
- }
-
- private func updateViewWithCNContact() {
- cnAddressBook.requestAccess(for: .contacts) { (granted, error) in
- if granted {
- let predicate: NSPredicate = CNContact.predicateForContacts(withIdentifiers: [(self.contact!.cnContactId)])
- let cnContactKeys = [CNContactFamilyNameKey, CNContactGivenNameKey, CNContactMiddleNameKey, CNContactOrganizationNameKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey, CNContactImageDataKey, CNContactImageDataAvailableKey, CNContactThumbnailImageDataKey, CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactViewController.descriptorForRequiredKeys()] as [Any]
- do {
- let contacts = try self.cnAddressBook.unifiedContacts(matching: predicate, keysToFetch: cnContactKeys as! [CNKeyDescriptor])
- if contacts.count > 0 {
- self.cnContact = contacts.first
- self.phoneCallNumbers()
- DispatchQueue.main.async {
- self.companyNameLabel.text = self.cnContact?.organizationName
- self.tableView.reloadData()
- }
- }
-
- }
- catch let err{
- DDLogNotice("Can't get CNContact form addressbook \(err.localizedDescription)")
- }
-
- }
- }
- }
-
- private func phoneCallNumbers() {
- if cnContact != nil {
- callNumbers = nil
- callNumbers = [String]()
- for phone: CNLabeledValue in cnContact!.phoneNumbers {
- let number = phone.value.stringValue
- if callNumbers!.contains(number) {
- continue
- }
- callNumbers?.append(number)
- }
- }
- }
-
- private func isExportConversationEnabled() {
- let mdmSetup = MDMSetup.init(setup: false)
- let entityManager = EntityManager.init()
- conversation = entityManager.entityFetcher.conversation(for: contact)
- canExportConversation = conversation != nil && !mdmSetup!.disableExport()
- }
-
- @objc private func tappedImage() {
- if contact != nil {
- var image: UIImage?
- if (contact!.contactImage != nil && UserSettings.shared().showProfilePictures) {
- image = UIImage.init(data: contact!.contactImage.data)
- }
- else if contact!.imageData != nil {
- image = UIImage.init(data: contact!.imageData)
- }
-
- if image != nil {
- guard let imageController = FullscreenImageViewController.init(for: image) else { return }
-
- if UIDevice.current.userInterfaceIdiom == .pad {
- let nav = ModalNavigationController.init(rootViewController: imageController)
- nav.showDoneButton = true
- nav.showFullScreenOnIPad = true
- present(nav, animated: true, completion: nil)
- } else {
- navigationController?.pushViewController(imageController, animated: true)
- }
- } else {
- tappedHeaderView()
- }
- }
- }
-
- @objc private func tappedHeaderView() {
- if contact != nil {
- if cnContact != nil {
- let personVC = CNContactViewController.init(for: cnContact!)
- personVC.allowsActions = true
- personVC.allowsEditing = true
- cnContactViewShowing = true
-
- if tabBarController?.tabBar.isHidden == false {
- didHideTabBar = true
- tabBarController?.tabBar.isHidden = true
- }
- let statusNavigationBar = navigationController?.navigationBar as! StatusNavigationBar
- statusNavigationBar.hideStatusView()
- navigationController?.navigationBar.barStyle = .default
- navigationController?.pushViewController(personVC, animated: true)
- } else {
- showEditContactVC()
- }
- }
- }
-
- private func showEditContactVC() {
- let editVC = storyboard!.instantiateViewController(withIdentifier: "EditContactViewController") as! EditContactViewController
- editVC.contact = contact
- navigationController?.pushViewController(editVC, animated: true)
- }
-
- private func conversationAction(sender: Any?) {
- let title = String(format: BundleUtil.localizedString(forKey: "include_media_title"), kExportConversationMediaSizeLimit)
- let actionSheet = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
-
- actionSheet.addAction(UIAlertAction(title: BundleUtil.localizedString(forKey: "include_media"), style: .default, handler: { (action) in
- let em = EntityManager()
- let exporter = ConversationExporter(viewController: self, contact: self.contact!, entityManager: em, withMedia: true)
- exporter.exportConversation()
-
- }))
-
- actionSheet.addAction(UIAlertAction(title: BundleUtil.localizedString(forKey: "without_media"), style: .default, handler: { (action) in
- let em = EntityManager()
- let exporter = ConversationExporter(viewController: self, contact: self.contact!, entityManager: em, withMedia: false)
- exporter.exportConversation()
- }))
-
- actionSheet.addAction(UIAlertAction(title: BundleUtil.localizedString(forKey: "cancel"), style: .cancel, handler: { (action) in
- self.tableView.deselectRow(at: IndexPath.init(row: 1, section: 1), animated: true)
- }))
-
- if sender is UIView {
- let senderView = sender as! UIView
- actionSheet.popoverPresentationController?.sourceRect = senderView.frame
- actionSheet.popoverPresentationController?.sourceView = view
- }
- AppDelegate.shared()?.currentTopViewController()?.present(actionSheet, animated: true, completion: nil)
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
- }
-
- private func startVoipCall(_ startWithVideo: Bool = false) {
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
-
- if ServerConnector.shared().connectionState == ConnectionStateLoggedIn {
- let action = VoIPCallUserAction.init(action: startWithVideo ? .callWithVideo : .call, contact:contact!, callId: nil, completion: nil)
- VoIPCallStateManager.shared.processUserAction(action)
- } else {
- let title = BundleUtil.localizedString(forKey: "cannot_connect_title")
- let message = BundleUtil.localizedString(forKey: "cannot_connect_message")
- UIAlertTemplate.showAlert(owner: self, title: title, message: message) { (action) in
- self.extensionContext?.completeRequest(returningItems: [Any](), completionHandler: nil)
- }
- }
- }
-
- private func makeTelUrlForPhone(_ phoneNumber: String) -> URL {
- let urlString = "tel:\(phoneNumber.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed) ?? "")"
- return URL.init(string: urlString)!
- }
-
- private func linkNewContact(view: UIView) {
- if cnContact != nil {
- let actionSheet = UIAlertController.init(title: nil, message: nil, preferredStyle: .actionSheet)
- actionSheet.addAction(UIAlertAction.init(title: BundleUtil.localizedString(forKey: "unlink_contact"), style: .destructive, handler: { (action) in
- ContactStore.shared()?.unlinkContact(self.contact)
- self.updateView()
- }))
- actionSheet.addAction(UIAlertAction.init(title: BundleUtil.localizedString(forKey: "choose_new_contact"), style: .default, handler: { (action) in
- self.linkNewContactCheckAuthorization()
- }))
- actionSheet.addAction(UIAlertAction.init(title: BundleUtil.localizedString(forKey: "cancel"), style: .cancel, handler: { (action) in
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
- }))
- actionSheet.popoverPresentationController?.sourceRect = view.frame
- actionSheet.popoverPresentationController?.sourceView = view
-
- present(actionSheet, animated: true, completion: nil)
- } else {
- linkNewContactCheckAuthorization()
- }
- }
-
- private func linkNewContactCheckAuthorization() {
- if CNContactStore.authorizationStatus(for: .contacts) != .authorized {
- cnAddressBook.requestAccess(for: .contacts) { (granted, error) in
- if granted {
- DispatchQueue.main.async {
- self.linkNewContactPick()
- }
- } else {
- DispatchQueue.main.async {
- let accessAlert = UIAlertController.init(title: BundleUtil.localizedString(forKey: "no_contacts_permission_title"), message: BundleUtil.localizedString(forKey: "no_contacts_permission_message"), preferredStyle: .alert)
- if self.contact!.cnContactId != nil {
- accessAlert.addAction(UIAlertAction.init(title: BundleUtil.localizedString(forKey: "unlink_contact"), style: .default, handler: { (action) in
- ContactStore.shared()?.unlinkContact(self.contact!)
- self.updateView()
- }))
- }
- accessAlert.addAction(UIAlertAction.init(title: BundleUtil.localizedString(forKey: "ok"), style: .default, handler: nil))
- self.present(accessAlert, animated: true, completion: nil)
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
- }
- }
- }
- } else {
- linkNewContactPick()
- }
- }
-
- private func linkNewContactPick() {
- let picker = CNContactPickerViewController.init()
- picker.delegate = self
- picker.modalPresentationStyle = .formSheet
- present(picker, animated: true, completion: nil)
- }
-
- private func shouldHideCell(_ indexPath: IndexPath) -> Bool{
- switch indexPath.section {
- case 0:
- switch indexPath.row {
- case 2:
- if contact?.publicNickname == nil {
- return true
- } else {
- if contact?.publicNickname.count == 0 || contact?.publicNickname == contact?.identity {
- return true
- }
- }
- break
- case 3:
- if contact!.isGatewayId() {
- return true
- }
- break
- default:
- break
- }
- break
- case 1:
- switch indexPath.row {
- case 1:
- if !UserSettings.shared().enableThreemaCall || is64Bit != 1 {
- return true
- }
- break
- case 2:
- if canExportConversation == false {
- return true
- }
- break
- case 3:
- if ScanIdentityController.canScan() == false {
- return true
- }
- break
- case 4:
- if contact!.isGatewayId() || contact!.isEchoEcho() || UserSettings.shared().sendProfilePicture == SendProfilePictureNone || (UserSettings.shared().sendProfilePicture == SendProfilePictureContacts && !UserSettings.shared().profilePictureContactList.contains(where: {($0 as! String) == contact!.identity})) {
- return true
- }
- break
- default:
- break
- }
- break
- default:
- break
- }
- return false
- }
-
- private func showWorkInfo(_ autoDismiss: Bool = true) {
- threemaTypeIcon.isHighlighted = false
- threemaTypeIcon.isSelected = false
- if showcase == nil {
- showcase = MaterialShowcase()
- showcase!.setTargetView(button: threemaTypeIcon)
- if LicenseStore.requiresLicenseKey() == false {
- showcase!.primaryText = BundleUtil.localizedString(forKey: "contact_threema_work_title")
- showcase!.secondaryText = BundleUtil.localizedString(forKey: "contact_threema_work_info")
- showcase!.backgroundPromptColor = Colors.workBlue()
- } else {
- showcase!.primaryText = BundleUtil.localizedString(forKey: "contact_threema_title")
- showcase!.secondaryText = BundleUtil.localizedString(forKey: "contact_threema_info")
- showcase!.backgroundPromptColor = Colors.green()
- }
-
- showcase!.backgroundPromptColorAlpha = 0.93
- showcase!.primaryTextSize = 24.0
- showcase!.secondaryTextSize = 20.0
- showcase!.primaryTextColor = Colors.white()
- showcase!.secondaryTextColor = Colors.white()
- showcase!.delegate = self
- }
- showcase!.show(completion: nil)
- if autoDismiss == true {
- DispatchQueue.main.asyncAfter(deadline: .now() + 6, execute: {
- if self.showcase != nil {
- self.showcase!.completeShowcase()
- }
- })
- }
- }
-
- // MARK: IBAction
-
- @IBAction func shareButtonTapped(sender: UIButton) {
- let contactShareLink = String.init(format: "%@%@", THREEMA_ID_SHARE_LINK, contact!.identity)
- let contactShareText = "\(contact!.displayName!): \(contactShareLink)"
- let activityViewController = UIActivityViewController.init(activityItems: [contactShareText], applicationActivities: nil)
- if UIDevice.current.userInterfaceIdiom == .pad {
- activityViewController.popoverPresentationController?.sourceRect = sender.frame
- activityViewController.popoverPresentationController?.sourceView = view
- }
- present(activityViewController, animated: true, completion: nil)
- }
-
- @IBAction func scanQrCodeAction(sender: UIButton) {
- scanIdentityAction()
- }
-
- @IBAction func workInfoButtonTapped(sender: UIButton) {
- showWorkInfo(false)
- }
- }
-
- extension ContactDetailsViewController {
- // MARK: - Table view data source
-
- override func numberOfSections(in tableView: UITableView) -> Int {
- if !(contact!.isGatewayId()) && !contact!.isEchoEcho() && UserSettings.shared()?.sendProfilePicture == SendProfilePictureContacts {
- return 4
- }
- return 3
- }
-
- override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- switch section {
- case 0:
- return 6
- case 1:
- return 5
- case 2:
- if !(contact!.isGatewayId()) && !contact!.isEchoEcho() && UserSettings.shared()?.sendProfilePicture == SendProfilePictureContacts {
- return 1
- } else {
- return 2
- }
- case 3:
- return 2
- default:
- return 0
- }
- }
-
- override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
- if section == 2 && !contact!.isGatewayId() && !contact!.isEchoEcho() && UserSettings.shared().sendProfilePicture == SendProfilePictureContacts {
- if UserSettings.shared().profilePictureContactList.contains(where: {($0 as! String) == contact!.identity}) {
- return BundleUtil.localizedString(forKey: "contact_added_to_profilepicture_list")
- } else {
- return BundleUtil.localizedString(forKey: "contact_removed_from_profilepicture_list")
- }
- }
- return nil
- }
-
- override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
- return shouldHideCell(indexPath) == true ? 0 : UITableView.automaticDimension
- }
-
- override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
- super.tableView(tableView, willDisplay: cell, forRowAt: indexPath)
-
- cell.isHidden = shouldHideCell(indexPath)
- }
-
- override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- switch indexPath.section {
- case 0:
- switch indexPath.row {
- case 0:
- let identityCell = tableView.dequeueReusableCell(withIdentifier: "IdentityCell")
- identityCell!.detailTextLabel?.text = contact?.identity
- identityCell!.detailTextLabel!.isAccessibilityElement = false
- let shareButton = identityCell?.accessoryView as! UIButton
- let shareImage: UIImage
- if #available(iOS 13.0, *) {
- shareImage = shareButton.imageView!.image!.withTintColor(Colors.main())
- } else {
- shareImage = shareButton.imageView!.image!.withTint(Colors.main())
- }
- shareButton.setImage(shareImage, for: .normal)
- return identityCell!
- case 1:
- let vlc = tableView.dequeueReusableCell(withIdentifier: "VerificationLevelCell") as! VerificationLevelCell
- vlc.contact = contact
- vlc.accessibilityTraits = .button
- return vlc
- case 2:
- let publicNicknameCell = tableView.dequeueReusableCell(withIdentifier: "PublicNicknameCell")
- publicNicknameCell!.detailTextLabel?.text = contact?.publicNickname
- return publicNicknameCell!
- case 3:
- let lcc = tableView.dequeueReusableCell(withIdentifier: "LinkedContactCell") as! LinkedContactCell
- lcc.accessibilityTraits = .button
-
- if cnContact != nil {
- lcc.displayNameLabel.text = CNContactFormatter.string(from: cnContact!, style: .fullName)
- if lcc.displayNameLabel.text == nil || lcc.displayNameLabel.text?.count == 0 {
- if cnContact!.emailAddresses.count > 0 {
- let first:CNLabeledValue = cnContact!.emailAddresses.first!
- lcc.displayNameLabel.text = first.value as String
- }
- }
- } else {
- lcc.displayNameLabel.text = BundleUtil.localizedString(forKey: "(none)")
- }
- return lcc
- case 4:
- let groupMembershipCell = tableView.dequeueReusableCell(withIdentifier: "GroupMembershipCell")
- groupMembershipCell?.textLabel?.text = BundleUtil.localizedString(forKey: "member_in_groups")
- groupMembershipCell?.detailTextLabel?.text = String.init(format: "%lu", contact?.groupConversations.count ?? 0)
- groupMembershipCell?.accessibilityTraits = .button
- return groupMembershipCell!
- case 5:
- let kfc = tableView.dequeueReusableCell(withIdentifier: "KeyFingerprintCell") as! KeyFingerprintCell
- kfc.fingerprintValueLabel.text = CryptoUtils.fingerprint(forPublicKey: contact?.publicKey)
- return kfc
- default:
- break
- }
- case 1:
- var cellIdentifier = ""
- switch indexPath.row {
- case 0:
- cellIdentifier = "SendMessageCell"
- break
- case 1:
- cellIdentifier = "ThreemaCallCell"
- break
- case 2:
- cellIdentifier = "ExportConversationCell"
- break
- case 3:
- cellIdentifier = "ScanIDCell"
- break
- case 4:
- cellIdentifier = "SendProfilePictureCell"
- break
- default:
- cellIdentifier = "SendMessageCell"
- }
- let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)
- cell?.accessibilityTraits = .button
- return cell!
- case 2:
- if !(contact!.isGatewayId()) && !contact!.isEchoEcho() && UserSettings.shared()?.sendProfilePicture == SendProfilePictureContacts {
- let profilePictureRecipientCell = tableView.dequeueReusableCell(withIdentifier: "ProfilePictureRecipientCell") as! ProfilePictureRecipientCell
- profilePictureRecipientCell.identity = contact?.identity
- profilePictureRecipientCell.delegate = self
- return profilePictureRecipientCell
- } else {
- switch indexPath.row {
- case 0:
- let pushSettingCell = tableView.dequeueReusableCell(withIdentifier: "PushSettingCell")
- pushSettingCell?.textLabel?.text = BundleUtil.localizedString(forKey: "pushSetting_title")
- return pushSettingCell!
- case 1:
- let bcc = tableView.dequeueReusableCell(withIdentifier: "BlockCell") as! BlockContactCell
- bcc.identity = contact?.identity
- return bcc
- default:
- break
- }
- }
- break
- case 3:
- switch indexPath.row {
- case 0:
- let pushSettingCell = tableView.dequeueReusableCell(withIdentifier: "PushSettingCell")
- pushSettingCell?.textLabel?.text = BundleUtil.localizedString(forKey: "pushSetting_title")
- return pushSettingCell!
- case 1:
- let bcc = tableView.dequeueReusableCell(withIdentifier: "BlockCell") as! BlockContactCell
- bcc.identity = contact?.identity
- return bcc
- default:
- break
- }
- break
- default:
- break
- }
- return UITableViewCell()
- }
-
- override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
- let cell = tableView.cellForRow(at: indexPath)
- switch indexPath.section {
- case 0:
- if indexPath.row == 3 {
- linkNewContact(view: cell!)
- }
- else if indexPath.row == 4 {
- let vc = storyboard!.instantiateViewController(withIdentifier: "contactGroupMembershipViewController") as! ContactGroupMembershipViewController
- vc.groupContact = contact
- navigationController?.pushViewController(vc, animated: true)
- }
- break
- case 1:
- if cell?.reuseIdentifier == "SendMessageCell" {
- sendMessageAction()
- }
- else if cell?.reuseIdentifier == "ThreemaCallCell" {
- startThreemaCallAction()
- }
- else if cell?.reuseIdentifier == "ExportConversationCell" {
- tableView.deselectRow(at: indexPath, animated: true)
- conversationAction(sender: cell)
- }
- else if cell?.reuseIdentifier == "ScanIDCell" {
- scanIdentityAction()
- }
- else if cell?.reuseIdentifier == "SendProfilePictureCell" {
- startProfilePictureAction()
- }
- break
- default:
- break
- }
- }
-
- override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
- if indexPath.section == 0 && indexPath.row == 1 {
- performSegue(withIdentifier: "VerificationSegue", sender: nil)
- }
- }
- }
- extension ContactDetailsViewController: ProfilePictureRecipientCellDelegate {
- func valueChanged(_ cell: ProfilePictureRecipientCell) {
- let indexSet: IndexSet = [1]
- tableView.beginUpdates()
- tableView.reloadSections(indexSet, with: .automatic)
- tableView.endUpdates()
-
- DispatchQueue.main.async {
- self.tableView.reloadData()
- }
- }
- }
- extension ContactDetailsViewController: CNContactPickerDelegate {
- func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
- let statusNavigationBar = navigationController?.navigationBar as! StatusNavigationBar
- statusNavigationBar.showOrHideStatusView()
- ContactStore.shared()?.linkContact(self.contact, toCnContactId: contact.identifier)
- dismiss(animated: true, completion: nil)
- updateView()
- }
-
- func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
- dismiss(animated: true, completion: nil)
- }
-
- func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
- if let selectedRow = self.tableView!.indexPathForSelectedRow {
- self.tableView.deselectRow(at: selectedRow, animated: true)
- }
- let statusNavigationBar = navigationController?.navigationBar as! StatusNavigationBar
- statusNavigationBar.showOrHideStatusView()
- dismiss(animated: true, completion: nil)
- }
- }
- extension ContactDetailsViewController: MaterialShowcaseDelegate {
- func showCaseWillDismiss(showcase: MaterialShowcase, didTapTarget: Bool) {
- UserSettings.shared()?.workInfoShown = true
- }
-
- func showCaseDidDismiss(showcase: MaterialShowcase, didTapTarget: Bool) {
- }
- }
|