From 96c50d93c715b29644f7b3e605568a33cd6f85b8 Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Tue, 13 Feb 2024 11:53:57 +0100 Subject: [PATCH] add a map when experimental 'location streaming' is enabled the map is available from profiles and from 'all media', showing positions, POIs and recent tracks --- DcCore/DcCore/DC/events.swift | 17 ++++ .../Extensions/UIColor+Extensions.swift | 12 +++ deltachat-ios.xcodeproj/project.pbxproj | 4 + deltachat-ios/Assets/maps.xdc | Bin 0 -> 52272 bytes .../Controller/AllMediaViewController.swift | 17 ++++ .../ContactDetailViewController.swift | 22 +++++ .../GroupChatDetailViewController.swift | 29 +++++- .../Controller/MapViewController.swift | 87 ++++++++++++++++++ .../Settings/AdvancedViewController.swift | 1 + .../Controller/WebxdcViewController.swift | 17 +++- deltachat-ios/DC/DcContext.swift | 82 ++++++++++++++++- deltachat-ios/DC/DcMsg.swift | 4 + .../ViewModel/ContactDetailViewModel.swift | 5 + 13 files changed, 286 insertions(+), 11 deletions(-) create mode 100644 deltachat-ios/Assets/maps.xdc create mode 100644 deltachat-ios/Controller/MapViewController.swift diff --git a/DcCore/DcCore/DC/events.swift b/DcCore/DcCore/DC/events.swift index f3dc260d5..70e2da513 100644 --- a/DcCore/DcCore/DC/events.swift +++ b/DcCore/DcCore/DC/events.swift @@ -13,6 +13,7 @@ public let eventEphemeralTimerModified = Notification.Name(rawValue: "eventEphe public let eventMsgsNoticed = Notification.Name(rawValue: "eventMsgsNoticed") public let eventConnectivityChanged = Notification.Name(rawValue: "eventConnectivityChanged") public let eventWebxdcStatusUpdate = Notification.Name(rawValue: "eventWebxdcStatusUpdate") +public let eventLocationChanged = Notification.Name(rawValue: "eventLocationChanged") public class DcEventHandler { let dcAccounts: DcAccounts @@ -174,6 +175,22 @@ public class DcEventHandler { ]) } + case DC_EVENT_LOCATION_CHANGED: + if accountId != dcAccounts.getSelected().id { + return + } + logger.info("📡[\(accountId)] location changed for contact \(data1)") + DispatchQueue.main.async { + let nc = NotificationCenter.default + nc.post( + name: eventLocationChanged, + object: nil, + userInfo: [ + "contact_id": Int(data1), + ] + ) + } + default: break } diff --git a/DcCore/DcCore/Extensions/UIColor+Extensions.swift b/DcCore/DcCore/Extensions/UIColor+Extensions.swift index 2cae4ad1b..2de0a517d 100644 --- a/DcCore/DcCore/Extensions/UIColor+Extensions.swift +++ b/DcCore/DcCore/Extensions/UIColor+Extensions.swift @@ -64,4 +64,16 @@ public extension UIColor { return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1) } + var hexValue: String { + var color = self + if color.cgColor.numberOfComponents < 4 { + let c = color.cgColor.components! + color = UIColor(red: c[0], green: c[0], blue: c[0], alpha: c[1]) + } + if color.cgColor.colorSpace!.model != .rgb { + return "#FFFFFF" + } + let c = color.cgColor.components! + return String(format: "#%02X%02X%02X", Int(c[0]*255.0), Int(c[1]*255.0), Int(c[2]*255.0)) + } } diff --git a/deltachat-ios.xcodeproj/project.pbxproj b/deltachat-ios.xcodeproj/project.pbxproj index c7f9141e3..d5c4667e1 100644 --- a/deltachat-ios.xcodeproj/project.pbxproj +++ b/deltachat-ios.xcodeproj/project.pbxproj @@ -195,6 +195,7 @@ AEFBE22F23FEF23D0045327A /* ProviderInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEFBE22E23FEF23D0045327A /* ProviderInfoCell.swift */; }; B20462E42440A4A600367A57 /* AutodelOverviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20462E32440A4A600367A57 /* AutodelOverviewViewController.swift */; }; B20462E62440C99600367A57 /* AutodelOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B20462E52440C99600367A57 /* AutodelOptionsViewController.swift */; }; + B206C2AB2B7B8088003ACBE6 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B206C2AA2B7B8088003ACBE6 /* MapViewController.swift */; }; B21005DB23383664004C70C5 /* EmailOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21005DA23383664004C70C5 /* EmailOptionsViewController.swift */; }; B2172F3C29C125F2002C289E /* AdvancedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2172F3B29C125F2002C289E /* AdvancedViewController.swift */; }; B259D64329B771D5008FB706 /* BackupTransferViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B259D64229B771D5008FB706 /* BackupTransferViewController.swift */; }; @@ -499,6 +500,7 @@ B20462E22440805C00367A57 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = ""; }; B20462E32440A4A600367A57 /* AutodelOverviewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutodelOverviewViewController.swift; sourceTree = ""; }; B20462E52440C99600367A57 /* AutodelOptionsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutodelOptionsViewController.swift; sourceTree = ""; }; + B206C2AA2B7B8088003ACBE6 /* MapViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = ""; }; B209B2042B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = ""; }; B209B2052B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; B209B2062B10139700FBBECF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -961,6 +963,7 @@ AEE6EC3E2282C59C00EDC689 /* GroupMembersViewController.swift */, 302E592326A5CF4800DD4F58 /* ConnectivityViewController.swift */, AE19887423EB264000B4CD5F /* HelpViewController.swift */, + B206C2AA2B7B8088003ACBE6 /* MapViewController.swift */, 304F769425DD237B0094B5E2 /* WebViewViewController.swift */, 304F769825DD23D70094B5E2 /* FullMessageViewController.swift */, 305501732798CDE1008FD5CA /* WebxdcViewController.swift */, @@ -1577,6 +1580,7 @@ B2C42570265C325C00B95377 /* MultilineLabelCell.swift in Sources */, AEF53BD5248904BF00D309C1 /* GalleryTimeLabel.swift in Sources */, AEE6EC482283045D00EDC689 /* SelfProfileViewController.swift in Sources */, + B206C2AB2B7B8088003ACBE6 /* MapViewController.swift in Sources */, 30653081254358B10093E196 /* QuoteView.swift in Sources */, 3067AAC62667F3FE00525036 /* ImageFormat.swift in Sources */, 30EF7308252F6A3300E2C54A /* PaddingTextView.swift in Sources */, diff --git a/deltachat-ios/Assets/maps.xdc b/deltachat-ios/Assets/maps.xdc new file mode 100644 index 0000000000000000000000000000000000000000..2fb5ca19140e471c9d341517229a28857062880d GIT binary patch literal 52272 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 z

KCMaziN8tX|-gZ*qt7g3Zufw%bJi_9X8#itRPly9BE>QQzW;2{wEVZ1OultW$t z*{{SgAzZIZGv8Tdd0zKN%|Gn&XE1{|Cd3I20?3!TRwJP8HD zZpFpf=p0>q>9hU)3|OnYe+0~xnQ?TEU@-@@vLtj#6I5S$+}zJ_Nk26?d_g>v?&&CXU^q6vj5YZ%cjGZcgWgup0Kmq zdaF$D57D2aWArp~2{)$S z4XDGvpR!LhX39RwRO%UKVPcp_M&)}E^)@A)Ii1mBu0*60vFWnpy z$ZC#|(1bOblM#F&ejVflhYHo)d)wOVdZ^4jMnfs2@b`H#%eP}YYyfo70o$IW zO-e$B_}w)j%t+~?zJ#cRL5KgrWmZyzOpJ$8$fq^_mZjnE zSy@)|ynq!{M@|bJfB*?Z>oKW+A@TF(re`juFLW%EHq@u)Q{ev z^oYc1j5Qh#L(nx&Pb>`!6eF%V-7C)ZQY@6nciWP6Axg9*T(_P~0$s^NM!s{FYoipT zFjawkXQ;zTkAcQ%V&(`)NzFi2mwTZ9!=4O=v=vw-$`Fc#Tf zU|<2f*|{c!#%Mx$PS9^gWRxVXMjfE7K%%dZgvAS{-LkYevtcY}dAJZoBUtb&_6!~C zy3ADhU2=+%5X=t{-kwKsI3C5<3>>}AF(|SqUEsuWsn{ z<_xbWr>m)w{&^cZ$A;=W1n3@3=Bm=Ur&6)`$1P6vY4HxV8$B4jf4zGCvj9~?6g7

<>{tEfBs_k{WnnfJ;Bi+jzBSD1M$HQp4Gi8Pec1 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�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 literal 0 HcmV?d00001 diff --git a/deltachat-ios/Controller/AllMediaViewController.swift b/deltachat-ios/Controller/AllMediaViewController.swift index bb841643c..bb4bc3ea0 100644 --- a/deltachat-ios/Controller/AllMediaViewController.swift +++ b/deltachat-ios/Controller/AllMediaViewController.swift @@ -47,6 +47,14 @@ class AllMediaViewController: UIPageViewController { return control }() + private lazy var mapButton: UIBarButtonItem = { + if #available(iOS 13.0, *) { + return UIBarButtonItem(image: UIImage(systemName: "map"), style: .plain, target: self, action: #selector(showMap)) + } else { + return UIBarButtonItem(title: String.localized("tab_map"), style: .plain, target: self, action: #selector(showMap)) + } + }() + init(dcContext: DcContext, chatId: Int = 0) { self.dcContext = dcContext self.chatId = chatId @@ -78,6 +86,11 @@ class AllMediaViewController: UIPageViewController { } } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationItem.rightBarButtonItem = chatId == 0 && UserDefaults.standard.bool(forKey: "location_streaming") ? mapButton : nil + } + // MARK: - actions @objc private func segmentControlChanged(_ sender: UISegmentedControl) { if sender.selectedSegmentIndex < pages.count { @@ -88,6 +101,10 @@ class AllMediaViewController: UIPageViewController { } } + @objc private func showMap() { + navigationController?.pushViewController(MapViewController(dcContext: dcContext, chatId: chatId), animated: true) + } + // MARK: - factory private func makeViewController(_ page: Page) -> UIViewController { if page.type1 == DC_MSG_IMAGE { diff --git a/deltachat-ios/Controller/ContactDetailViewController.swift b/deltachat-ios/Controller/ContactDetailViewController.swift index 1c404f535..356a52b67 100644 --- a/deltachat-ios/Controller/ContactDetailViewController.swift +++ b/deltachat-ios/Controller/ContactDetailViewController.swift @@ -95,6 +95,20 @@ class ContactDetailViewController: UITableViewController { return cell }() + private lazy var locationsCell: UITableViewCell = { + let cell = UITableViewCell(style: .value1, reuseIdentifier: nil) + cell.textLabel?.text = String.localized("locations") + if #available(iOS 13.0, *) { + cell.imageView?.image = UIImage(systemName: "map") // added in ios13 + } + cell.accessoryType = .disclosureIndicator + if viewModel.chatId == 0 { + cell.isUserInteractionEnabled = false + cell.textLabel?.isEnabled = false + } + return cell + }() + private lazy var statusCell: MultilineLabelCell = { let cell = MultilineLabelCell() cell.multilineDelegate = self @@ -179,6 +193,8 @@ class ContactDetailViewController: UITableViewController { return verifiedByCell case .allMedia: return allMediaCell + case .locations: + return locationsCell case .ephemeralMessages: return ephemeralMessagesCell case .startChat: @@ -368,6 +384,8 @@ class ContactDetailViewController: UITableViewController { } case .allMedia: showAllMedia() + case .locations: + showLocations() case .ephemeralMessages: showEphemeralMessagesController() case .startChat: @@ -523,6 +541,10 @@ class ContactDetailViewController: UITableViewController { navigationController?.pushViewController(AllMediaViewController(dcContext: viewModel.context, chatId: viewModel.chatId), animated: true) } + private func showLocations() { + navigationController?.pushViewController(MapViewController(dcContext: viewModel.context, chatId: viewModel.chatId), animated: true) + } + private func showSearch() { if let chatViewController = navigationController?.viewControllers.last(where: { $0 is ChatViewController diff --git a/deltachat-ios/Controller/GroupChatDetailViewController.swift b/deltachat-ios/Controller/GroupChatDetailViewController.swift index ea84315ca..e0f6e3390 100644 --- a/deltachat-ios/Controller/GroupChatDetailViewController.swift +++ b/deltachat-ios/Controller/GroupChatDetailViewController.swift @@ -13,6 +13,7 @@ class GroupChatDetailViewController: UIViewController { enum ChatOption { case allMedia + case locations case ephemeralMessages } @@ -142,6 +143,16 @@ class GroupChatDetailViewController: UIViewController { return cell }() + private lazy var locationsCell: UITableViewCell = { + let cell = UITableViewCell(style: .value1, reuseIdentifier: nil) + cell.textLabel?.text = String.localized("locations") + if #available(iOS 13.0, *) { + cell.imageView?.image = UIImage(systemName: "map") // added in ios13 + } + cell.accessoryType = .disclosureIndicator + return cell + }() + init(chatId: Int, dcContext: DcContext) { self.dcContext = dcContext self.chatId = chatId @@ -279,23 +290,25 @@ class GroupChatDetailViewController: UIViewController { private func updateOptions() { self.editBarButtonItem.isEnabled = chat.isMailinglist || chat.canSend + self.chatOptions = [.allMedia] + if UserDefaults.standard.bool(forKey: "location_streaming") { + self.chatOptions.append(.locations) + } + if chat.isMailinglist { - self.chatOptions = [.allMedia] self.memberManagementRows = 0 self.chatActions = [.archiveChat, .copyToClipboard, .clearChat, .deleteChat] self.groupHeader.showMuteButton(show: true) } else if chat.isBroadcast { - self.chatOptions = [.allMedia] self.memberManagementRows = 1 self.chatActions = [.archiveChat, .cloneChat, .clearChat, .deleteChat] self.groupHeader.showMuteButton(show: false) } else if chat.canSend { - self.chatOptions = [.allMedia, .ephemeralMessages] + self.chatOptions.append(.ephemeralMessages) self.memberManagementRows = 2 self.chatActions = [.archiveChat, .cloneChat, .leaveGroup, .clearChat, .deleteChat] self.groupHeader.showMuteButton(show: true) } else { - self.chatOptions = [.allMedia] self.memberManagementRows = 0 self.chatActions = [.archiveChat, .clearChat, .deleteChat] self.groupHeader.showMuteButton(show: true) @@ -416,6 +429,10 @@ class GroupChatDetailViewController: UIViewController { navigationController?.pushViewController(AllMediaViewController(dcContext: dcContext, chatId: chatId), animated: true) } + private func showLocations() { + navigationController?.pushViewController(MapViewController(dcContext: dcContext, chatId: chatId), animated: true) + } + private func showSearch() { if let chatViewController = navigationController?.viewControllers.last(where: { $0 is ChatViewController @@ -478,6 +495,8 @@ extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSou switch chatOptions[row] { case .allMedia: return allMediaCell + case .locations: + return locationsCell case .ephemeralMessages: return ephemeralMessagesCell } @@ -538,6 +557,8 @@ extension GroupChatDetailViewController: UITableViewDelegate, UITableViewDataSou switch chatOptions[row] { case .allMedia: showAllMedia() + case .locations: + showLocations() case .ephemeralMessages: showEphemeralMessagesController() } diff --git a/deltachat-ios/Controller/MapViewController.swift b/deltachat-ios/Controller/MapViewController.swift new file mode 100644 index 000000000..d05ea61e4 --- /dev/null +++ b/deltachat-ios/Controller/MapViewController.swift @@ -0,0 +1,87 @@ +import UIKit +import WebKit +import DcCore + +class MapViewController: WebxdcViewController { + private let chatId: Int + private var locationChangedObserver: NSObjectProtocol? + private var lastLocationId: Int = 0 + + init(dcContext: DcContext, chatId: Int) { + self.chatId = chatId + let msgIdConfigKey = "maps_webxdc_msg_id16." + var msgId = UserDefaults.standard.integer(forKey: msgIdConfigKey + String(dcContext.id)) + if !dcContext.msgExists(id: msgId) { + if let path = Bundle.main.url(forResource: "maps", withExtension: "xdc", subdirectory: "Assets") { + let chatId = dcContext.createChatByContactId(contactId: Int(DC_CONTACT_ID_SELF)) + let msg = dcContext.newMessage(viewType: DC_MSG_WEBXDC) + msg.setFile(filepath: path.path) + msgId = dcContext.sendMessage(chatId: chatId, message: msg) // TODO: this should be hidden by core somehow + UserDefaults.standard.setValue(msgId, forKey: msgIdConfigKey + String(dcContext.id)) + } + } + super.init(dcContext: dcContext, messageId: msgId) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.rightBarButtonItem = nil + } + + override func willMove(toParent parent: UIViewController?) { + super.willMove(toParent: parent) + if parent == nil { + removeObservers() + } else { + addObservers() + } + } + + override func refreshWebxdcInfo() { + super.refreshWebxdcInfo() + title = String.localized(chatId == 0 ? "menu_show_global_map" : "locations") + } + + // MARK: - setup + + private func addObservers() { + locationChangedObserver = NotificationCenter.default.addObserver(forName: eventLocationChanged, object: nil, queue: nil) { [weak self]_ in + self?.updateWebxdc() + } + } + + private func removeObservers() { + if let locationChangedObserver = self.locationChangedObserver { + NotificationCenter.default.removeObserver(locationChangedObserver) + } + } + + + // MARK: - handle updates + + override func sendWebxdcStatusUpdate(payload: String, description: String) -> Bool { + guard let data: Data = payload.data(using: .utf8), + let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: AnyObject], + let payload = dict["payload"] as? [String: AnyObject] else { + return false + } + + let msg = dcContext.newMessage(viewType: DC_MSG_TEXT) + msg.text = payload["label"] as? String ?? "ErrLabel" + msg.setLocation(lat: payload["lat"] as? Double ?? 0.0, lng: payload["lng"] as? Double ?? 0.0) + return dcContext.sendMessage(chatId: chatId == 0 ? dcContext.createChatByContactId(contactId: Int(DC_CONTACT_ID_SELF)) : chatId, message: msg) != 0 + } + + override func getWebxdcStatusUpdates(lastKnownSerial: Int) -> String { + let end = Int64(Date().timeIntervalSince1970) + let begin = end - 24*60*60 + let (json, maxLocationId) = dcContext.getLocations(chatId: chatId, timestampBegin: begin, timestampEnd: 0, lastLocationId: lastLocationId) + lastLocationId = max(maxLocationId, lastLocationId) + UIPasteboard.general.string = json // TODO: remove this line, useful for debugging to get JSON out + return json + } +} diff --git a/deltachat-ios/Controller/Settings/AdvancedViewController.swift b/deltachat-ios/Controller/Settings/AdvancedViewController.swift index d136a99ee..bb32d88bc 100644 --- a/deltachat-ios/Controller/Settings/AdvancedViewController.swift +++ b/deltachat-ios/Controller/Settings/AdvancedViewController.swift @@ -329,6 +329,7 @@ internal final class AdvancedViewController: UITableViewController, ProgressAler if !locationStreaming { let alert = UIAlertController(title: "Thanks for trying out the experimental feature 🧪 \"Location streaming\"", message: "You will find a corresponding option in the attach menu (the paper clip) of each chat now.\n\n" + + "Moreover, \"Profiles\" and \"All Media\" will offer a map.\n\n" + "If you want to quit the experimental feature, you can disable it at \"Settings / Advanced\".", preferredStyle: .alert) alert.addAction(UIAlertAction(title: String.localized("ok"), style: .default, handler: nil)) diff --git a/deltachat-ios/Controller/WebxdcViewController.swift b/deltachat-ios/Controller/WebxdcViewController.swift index 542578032..224acd6e0 100644 --- a/deltachat-ios/Controller/WebxdcViewController.swift +++ b/deltachat-ios/Controller/WebxdcViewController.swift @@ -264,7 +264,7 @@ class WebxdcViewController: WebViewViewController { } } - private func refreshWebxdcInfo() { + func refreshWebxdcInfo() { let msg = dcContext.getMessage(id: messageId) let dict = msg.getWebxdcInfoDict() @@ -372,7 +372,7 @@ class WebxdcViewController: WebViewViewController { } } - private func updateWebxdc() { + func updateWebxdc() { webView.evaluateJavaScript("window.__webxdcUpdate()", completionHandler: nil) } @@ -414,6 +414,14 @@ class WebxdcViewController: WebViewViewController { private func shareWebxdc(_ action: UIAlertAction) { Utils.share(message: dcContext.getMessage(id: messageId), parentViewController: self, sourceItem: moreButton) } + + func sendWebxdcStatusUpdate(payload: String, description: String) -> Bool { + return dcContext.sendWebxdcStatusUpdate(msgId: messageId, payload: payload, description: description) + } + + func getWebxdcStatusUpdates(lastKnownSerial: Int) -> String { + return dcContext.getWebxdcStatusUpdates(msgId: messageId, lastKnownSerial: lastKnownSerial) + } } extension WebxdcViewController: WKScriptMessageHandler { @@ -436,7 +444,7 @@ extension WebxdcViewController: WKScriptMessageHandler { logger.error("Failed to parse status update parameters \(message.body)") return } - _ = dcContext.sendWebxdcStatusUpdate(msgId: messageId, payload: payloadString, description: description) + _ = sendWebxdcStatusUpdate(payload: payloadString, description: description) case .sendToChat: if let dict = message.body as? [String: AnyObject] { @@ -477,8 +485,7 @@ extension WebxdcViewController: WKURLSchemeHandler { let statusCode: Int if url.path == "/webxdc-update.json" || url.path == "webxdc-update.json" { let lastKnownSerial = Int(url.query ?? "0") ?? 0 - data = Data( - dcContext.getWebxdcStatusUpdates(msgId: messageId, lastKnownSerial: lastKnownSerial).utf8) + data = Data(getWebxdcStatusUpdates(lastKnownSerial: lastKnownSerial).utf8) mimeType = "application/json; charset=utf-8" statusCode = 200 } else { diff --git a/deltachat-ios/DC/DcContext.swift b/deltachat-ios/DC/DcContext.swift index c17dd4588..621926fb4 100644 --- a/deltachat-ios/DC/DcContext.swift +++ b/deltachat-ios/DC/DcContext.swift @@ -54,13 +54,25 @@ public class DcContext { return DcMsg(pointer: messagePointer) } + public func msgExists(id: Int) -> Bool { + if id <= DC_MSG_ID_LAST_SPECIAL { + return false + } else { + let messagePointer = dc_get_msg(contextPointer, UInt32(id)) + let exists = messagePointer != nil && dc_msg_get_chat_id(messagePointer) != DC_CHAT_ID_TRASH + dc_msg_unref(messagePointer) + return exists + } + } + public func getMessage(id: Int) -> DcMsg { let messagePointer = dc_get_msg(contextPointer, UInt32(id)) return DcMsg(pointer: messagePointer) } - public func sendMessage(chatId: Int, message: DcMsg) { - dc_send_msg(contextPointer, UInt32(chatId), message.messagePointer) + @discardableResult + public func sendMessage(chatId: Int, message: DcMsg) -> Int { + return Int(dc_send_msg(contextPointer, UInt32(chatId), message.messagePointer)) } public func downloadFullMessage(id: Int) { @@ -532,6 +544,72 @@ public class DcContext { dc_set_location(contextPointer, latitude, longitude, accuracy) } + public func getLocations(chatId: Int, timestampBegin: Int64, timestampEnd: Int64, lastLocationId: Int) -> (String, Int) { + var names = [Int: String]() + var colors = [Int: String]() + var maxLocationId = 0 + + let jsonEncoder = JSONEncoder() + var json: String + json = "[" + let array = dc_get_locations(contextPointer, UInt32(chatId), 0, timestampBegin, timestampEnd) + let arrayCnt = dc_array_get_cnt(array) + for i in (0 ..< arrayCnt).reversed() { // most recent position is reported last + let locationId = Int(dc_array_get_id(array, i)) + if locationId > lastLocationId { + maxLocationId = max(maxLocationId, locationId) + if json != "[" { + json += "," // JSON is picky about commas after the last element + } + + let contactId = Int(dc_array_get_contact_id(array, i)) + if names[contactId] == nil { + let contact = getContact(id: contactId) + names[contactId] = contact.displayName + colors[contactId] = contact.color.hexValue + } + + let isIndependet = dc_array_is_independent(array, i) != 0 + var label: String = "" + let name: String = names[contactId] ?? "ErrName" + if isIndependet { + if let cString = dc_array_get_marker(array, i) { + label = String(cString: cString) // get_marker() returns one-char labels only + dc_str_unref(cString) + } else { + let msgId = Int(dc_array_get_msg_id(array, i)) + if msgId != 0 { + label = String((getMessage(id: msgId).text ?? "").prefix(256)) + } + } + } + + let jsonName = (try? String(data: jsonEncoder.encode(name), encoding: .utf8) ?? "\"\"") ?? "\"\"" + let jsonLabel = (try? String(data: jsonEncoder.encode(label), encoding: .utf8) ?? "\"\"") ?? "\"\"" + json += """ + { + "payload": { + "action": "pos", + "contactId": \(contactId), + "lat": \(dc_array_get_latitude(array, i)), + "lng": \(dc_array_get_longitude(array, i)), + "independent": \(isIndependet), + "timestamp": \(dc_array_get_timestamp(array, i)), + "label": \(jsonLabel), + "name": \(jsonName), + "color": "\(colors[contactId] ?? "#ff0000")" + }, + "serial": \(locationId), + "max_serial": \(locationId) + } + """ + } + } + dc_array_unref(array) + json += "]" + return (json, maxLocationId) + } + public func searchMessages(chatId: Int = 0, searchText: String) -> [Int] { let start = CFAbsoluteTimeGetCurrent() guard let arrayPointer = dc_search_msgs(contextPointer, UInt32(chatId), searchText) else { diff --git a/deltachat-ios/DC/DcMsg.swift b/deltachat-ios/DC/DcMsg.swift index 190dea60d..da534d087 100644 --- a/deltachat-ios/DC/DcMsg.swift +++ b/deltachat-ios/DC/DcMsg.swift @@ -226,6 +226,10 @@ public class DcMsg { dc_msg_set_dimension(messagePointer, Int32(width), Int32(height)) } + public func setLocation(lat: Double, lng: Double) { + dc_msg_set_location(messagePointer, lat, lng) + } + public var filesize: Int { return Int(dc_msg_get_filebytes(messagePointer)) } diff --git a/deltachat-ios/ViewModel/ContactDetailViewModel.swift b/deltachat-ios/ViewModel/ContactDetailViewModel.swift index d0e8e3ca9..dcfc4f7e4 100644 --- a/deltachat-ios/ViewModel/ContactDetailViewModel.swift +++ b/deltachat-ios/ViewModel/ContactDetailViewModel.swift @@ -15,6 +15,7 @@ class ContactDetailViewModel { enum ChatOption { case verifiedBy case allMedia + case locations case ephemeralMessages case startChat } @@ -80,6 +81,10 @@ class ContactDetailViewModel { chatOptions.append(.verifiedBy) } chatOptions.append(.allMedia) + if UserDefaults.standard.bool(forKey: "location_streaming") { + chatOptions.append(.locations) + } + if chatId != 0 { if !isDeviceTalk { chatOptions.append(.ephemeralMessages)