diff --git a/Nynja-Share/UI/ForwardSelectorInteractor.swift b/Nynja-Share/UI/ForwardSelectorInteractor.swift index 0e9293cf67b59bf38541e61285f5cb211ee7a871..ad930c42f87fb3977ee7d394c1d7e7b9a53e1e3f 100644 --- a/Nynja-Share/UI/ForwardSelectorInteractor.swift +++ b/Nynja-Share/UI/ForwardSelectorInteractor.swift @@ -73,14 +73,26 @@ final class ForwardSelectorInteractor: ForwardSelectorInteractorInputProtocol, P private var contacts: [ForwardTarget]? private var groups: [ForwardTarget]? - required init(mode: ForwardSelectorMode) { self.mode = mode ProfileHandler.delegate = self MessageHandler.delegate = self ContactHandler.delegate = self IoHandler.delegate = self - setupAmazon() + setupAmazon() + notifyHostApplication() + } + + func notifyHostApplication() { + guard let container = AppGroupFlagContainer(fileManager: .default, appGroup: Bundle.main.appGroupName) else { + return + } + do { + try container.prepare() + try container.setFlag(.shareExtension) + } catch { + LogService.log(topic: .fileSystem) { error.localizedDescription } + } } func connectToServer() { diff --git a/Nynja-Share/UI/ForwardSelectorViewController.swift b/Nynja-Share/UI/ForwardSelectorViewController.swift index 5a85c781de80006e426defe816108f05bebb128a..e23b724bd427e8d9f64a065d412dc8972f187bf6 100644 --- a/Nynja-Share/UI/ForwardSelectorViewController.swift +++ b/Nynja-Share/UI/ForwardSelectorViewController.swift @@ -290,6 +290,7 @@ final class ForwardSelectorViewController: UIViewController, ForwardSelectorView override func viewDidLoad() { super.viewDidLoad() + getAttachmentContent() presenter.view = self presenter.interactor.handlerServerSignals = { [weak self] signal in diff --git a/Nynja.xcodeproj/project.pbxproj b/Nynja.xcodeproj/project.pbxproj index 179cef84f2b6c94ab420ddfb0319f678eaf8fdf0..dc4105519155d7ef6b64f63819a346784e5e6269 100644 --- a/Nynja.xcodeproj/project.pbxproj +++ b/Nynja.xcodeproj/project.pbxproj @@ -723,6 +723,10 @@ 850833DB2037171600587EEF /* FileExtensionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850833DA2037171600587EEF /* FileExtensionView.swift */; }; 8509452B206E684300B43C1C /* AddParticipantsContactCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509452A206E684300B43C1C /* AddParticipantsContactCell.swift */; }; 8509AC62206A54420089089B /* ResponseResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509AC61206A54420089089B /* ResponseResult.swift */; }; + 8509FC852158F7D100734D93 /* AppGroupFlagContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC842158F7D100734D93 /* AppGroupFlagContainer.swift */; }; + 8509FC872158F7FC00734D93 /* DirectoryWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC862158F7FC00734D93 /* DirectoryWatcher.swift */; }; + 8509FC89215908B300734D93 /* AppGroupFlagObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC88215908B300734D93 /* AppGroupFlagObserver.swift */; }; + 8509FC8A2159095900734D93 /* AppGroupFlagContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8509FC842158F7D100734D93 /* AppGroupFlagContainer.swift */; }; 850A0C6520469AED004F79AD /* UserSettingsRespondable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850A0C6420469AED004F79AD /* UserSettingsRespondable.swift */; }; 850A0C672046B65D004F79AD /* WCItemsFactoryDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850A0C662046B65D004F79AD /* WCItemsFactoryDecorator.swift */; }; 850A2BB0203584B000D68FDF /* SearchActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850A2BAF203584B000D68FDF /* SearchActionsView.swift */; }; @@ -2816,6 +2820,9 @@ 850833DA2037171600587EEF /* FileExtensionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileExtensionView.swift; sourceTree = ""; }; 8509452A206E684300B43C1C /* AddParticipantsContactCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddParticipantsContactCell.swift; sourceTree = ""; }; 8509AC61206A54420089089B /* ResponseResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseResult.swift; sourceTree = ""; }; + 8509FC842158F7D100734D93 /* AppGroupFlagContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroupFlagContainer.swift; sourceTree = ""; }; + 8509FC862158F7FC00734D93 /* DirectoryWatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectoryWatcher.swift; sourceTree = ""; }; + 8509FC88215908B300734D93 /* AppGroupFlagObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroupFlagObserver.swift; sourceTree = ""; }; 850A0C6420469AED004F79AD /* UserSettingsRespondable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsRespondable.swift; sourceTree = ""; }; 850A0C662046B65D004F79AD /* WCItemsFactoryDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WCItemsFactoryDecorator.swift; sourceTree = ""; }; 850A2BAF203584B000D68FDF /* SearchActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchActionsView.swift; sourceTree = ""; }; @@ -5635,6 +5642,7 @@ 3A768E1C1ECD152300108F7C /* Services */ = { isa = PBXGroup; children = ( + 8509FC832158F7B400734D93 /* Files */, 4BE2C5E42142EB5A00A73DD9 /* NynjaCalls */, 4BE2C5CE2142EAC500A73DD9 /* Audio */, 4B0DBA892137F6F800D79163 /* ChatService */, @@ -7171,6 +7179,16 @@ path = ThemePicker; sourceTree = ""; }; + 8509FC832158F7B400734D93 /* Files */ = { + isa = PBXGroup; + children = ( + 8509FC842158F7D100734D93 /* AppGroupFlagContainer.swift */, + 8509FC88215908B300734D93 /* AppGroupFlagObserver.swift */, + 8509FC862158F7FC00734D93 /* DirectoryWatcher.swift */, + ); + path = Files; + sourceTree = ""; + }; 850C3015204DA84400DB26C2 /* Privacy */ = { isa = PBXGroup; children = ( @@ -13372,6 +13390,7 @@ 85E3AB3D21218A57005FC49A /* SeparatorView.swift in Sources */, A47785A220D18D4A0053E0D2 /* BaseView.swift in Sources */, A42CE58020692EDB000889CC /* error2.swift in Sources */, + 8509FC8A2159095900734D93 /* AppGroupFlagContainer.swift in Sources */, A42CE58820692EDB000889CC /* Cursor.swift in Sources */, A49E1BD320A9AA0E0074DFD3 /* BaseChatModel.swift in Sources */, A42CE5C820692EDB000889CC /* act_Spec.swift in Sources */, @@ -13679,6 +13698,7 @@ A42CE5AF20692EDB000889CC /* TypeSpec.swift in Sources */, FB0B721320907DB5003B9757 /* MessageEditService.swift in Sources */, 859B862C204820DC003272B2 /* ThemePickerPresenter.swift in Sources */, + 8509FC872158F7FC00734D93 /* DirectoryWatcher.swift in Sources */, 2603139B20A0A4BA009AC66D /* LanguageSelectorViewController.swift in Sources */, 2686D3201FC3E39C0079CB75 /* ContentNavigationVC.swift in Sources */, B7EF8EDB210C759400E0E981 /* InterpretationTypeCellModel.swift in Sources */, @@ -14778,6 +14798,7 @@ 26B32B961FE20BAB00888A0A /* DescExtension+BERT.swift in Sources */, 4B1D7E0B2029D8CD00703228 /* GroupOptionsItemsFactory.swift in Sources */, CCF8AA193F15D4191EC99051 /* SplashProtocols.swift in Sources */, + 8509FC852158F7D100734D93 /* AppGroupFlagContainer.swift in Sources */, 26342CAF20ECD16A00D2196B /* TranscribeShortResponseData.swift in Sources */, E743B5881FB08F0F00F72F92 /* ParticipantsAvatarCell.swift in Sources */, D883A2CBD629A340B27997EF /* SplashViewController.swift in Sources */, @@ -15130,6 +15151,7 @@ A432CF1F20B44C0000993AFB /* MaterialTextContainer.swift in Sources */, 5AD8110B5B87B1AB9F1C5B52 /* CreateGroupPresenter.swift in Sources */, A43B259520AB1DFA00FF8107 /* InputContentProtocol.swift in Sources */, + 8509FC89215908B300734D93 /* AppGroupFlagObserver.swift in Sources */, 2625DBF620EFC52E00E01C05 /* AudioFileConvertOperation.swift in Sources */, 0062D9482062EC4100B915AC /* InviteFriendsInteractor.swift in Sources */, 8562853220D140FC000C9739 /* InputBar+ButtonType.swift in Sources */, diff --git a/Nynja/Files/AppGroupFlagContainer.swift b/Nynja/Files/AppGroupFlagContainer.swift new file mode 100644 index 0000000000000000000000000000000000000000..f7341dca5db7b91ef06749cda3028fb54f09d555 --- /dev/null +++ b/Nynja/Files/AppGroupFlagContainer.swift @@ -0,0 +1,66 @@ +// +// AppGroupFlagContainer.swift +// Nynja +// +// Created by Anton Poltoratskyi on 24.09.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +class AppGroupFlagContainer { + + enum Flag: String { + case shareExtension = "share-extension.lock" + + var fileName: String { + return rawValue + } + } + + let fileManager: FileManager + + let containerURL: URL + + var flagsDirectoryURL: URL { + return containerURL.appendingPathComponent("flags") + } + + + // MARK: - Init + + init?(fileManager: FileManager, appGroup: String) { + self.fileManager = fileManager + + guard let containerURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else { + return nil + } + self.containerURL = containerURL + } + + func prepare() throws { + if !fileManager.fileExists(atPath: flagsDirectoryURL.path) { + try fileManager.createDirectory(at: flagsDirectoryURL, withIntermediateDirectories: true, attributes: nil) + } + } + + + // MARK: - Flags + + func setFlag(_ flag: Flag) throws { + let flagURL = flagsDirectoryURL.appendingPathComponent(flag.fileName) + + // From Apple docs: + // If a file already exists at path, this method overwrites the contents of that file + // if the current process has the appropriate privileges to do so. + fileManager.createFile(atPath: flagURL.path, contents: nil, attributes: nil) + } + + func removeFlagIfExists(_ flag: Flag) throws { + let flagURL = flagsDirectoryURL.appendingPathComponent(flag.fileName) + guard fileManager.fileExists(atPath: flagURL.path) else { + return + } + try fileManager.removeItem(atPath: flagURL.path) + } +} diff --git a/Nynja/Files/AppGroupFlagObserver.swift b/Nynja/Files/AppGroupFlagObserver.swift new file mode 100644 index 0000000000000000000000000000000000000000..d934c1f35e20b1d013d609bb0ad608852d9e2457 --- /dev/null +++ b/Nynja/Files/AppGroupFlagObserver.swift @@ -0,0 +1,40 @@ +// +// AppGroupFlagObserver.swift +// Nynja +// +// Created by Anton Poltoratskyi on 24.09.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +final class AppGroupFlagObserver: AppGroupFlagContainer { + + private var directoryWatcher: DirectoryWatcher? + + + // MARK: - Init + + deinit { + stopObserving() + } + + func observe(callback: @escaping ([Flag]) -> Void) throws { + try prepare() + + directoryWatcher = DirectoryWatcher.makeDirectoryWatcher(withPath: flagsDirectoryURL.path) { [weak self] watcher in + guard let `self` = self else { + return + } + do { + let content = try self.fileManager.contentsOfDirectory(atPath: self.flagsDirectoryURL.path) + let flags = content.compactMap { Flag(rawValue: $0) } + callback(flags) + } catch { } + } + } + + func stopObserving() { + directoryWatcher?.stop() + } +} diff --git a/Nynja/Files/DirectoryWatcher.swift b/Nynja/Files/DirectoryWatcher.swift new file mode 100644 index 0000000000000000000000000000000000000000..f6950e4716dac3f18be45c1db7f97104794d26cf --- /dev/null +++ b/Nynja/Files/DirectoryWatcher.swift @@ -0,0 +1,96 @@ +// +// DirectoryWatcher.swift +// Nynja +// +// Created by Anton Poltoratskyi on 24.09.2018. +// Copyright © 2018 TecSynt Solutions. All rights reserved. +// + +import Foundation + +public class DirectoryWatcher { + + public typealias Callback = (DirectoryWatcher) -> Void + + private var directoryFileDescriptor: Int32 = -1 { + didSet { + if oldValue != -1 { + close(oldValue) + } + } + } + + private var dispatchSource: DispatchSourceFileSystemObject? + + private let queue: DispatchQueue + + + // MARK: - Init + + private init(queue: DispatchQueue) { + self.queue = queue + } + + deinit { + stop() + } + + public static func makeDirectoryWatcher(withPath path: String, + onQueue queue: DispatchQueue = .main, + callback: @escaping Callback) -> DirectoryWatcher? { + let directoryWatcher = DirectoryWatcher(queue: queue) + + if !directoryWatcher.watch(path: path, callback: callback) { + assertionFailure() + return nil + } + + return directoryWatcher + } + + + // MARK: - Watch + + private func watch(path: String, callback: @escaping Callback) -> Bool { + // Open the directory + directoryFileDescriptor = open(path, O_EVTONLY) + if directoryFileDescriptor < 0 { + return false + } + + // Create and configure a DispatchSource to monitor it + let dispatchSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: directoryFileDescriptor, + eventMask: .write, + queue: queue) + dispatchSource.setEventHandler { [unowned self] in + callback(self) + } + dispatchSource.setCancelHandler { [unowned self] in + self.directoryFileDescriptor = -1 + } + self.dispatchSource = dispatchSource + + // Start monitoring + dispatchSource.resume() + + // Success + return true + } + + public func stop() { + // Leave if not monitoring + guard let dispatchSource = dispatchSource else { + return + } + + // Don't listen to more events + dispatchSource.setEventHandler(handler: nil) + + // Cancel the source (this will also close the directory) + dispatchSource.cancel() + + dispatchSource.setCancelHandler(handler: nil) + + self.dispatchSource = nil + } +} diff --git a/Nynja/Library/UI/AlertManager.swift b/Nynja/Library/UI/AlertManager.swift index cbea0456e7d852de3b2a90442b63ec89a2d7bace..19e17b0c184c337ab1b4872414413d31441b32d1 100644 --- a/Nynja/Library/UI/AlertManager.swift +++ b/Nynja/Library/UI/AlertManager.swift @@ -11,7 +11,7 @@ import UIKit class AlertManager { - static let sharedInstance : AlertManager = { + static let sharedInstance: AlertManager = { let instance = AlertManager() return instance }() @@ -20,6 +20,9 @@ class AlertManager { return UIApplication.shared.keyWindow?.presentedViewController } + // FIXME: need to be removed from here when share extension won't require new mqtt connection. + private var appGroupObserver: AppGroupFlagObserver? + func showAlertOk(title: String, message: String, completion:(()->Void)? = nil) { let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) let defaultAction = UIAlertAction(title: "ok".localized, style: .default) { (action) in @@ -189,9 +192,31 @@ class AlertManager { } func showNativeShare(with activityItems: [Any]) { + var shareExtensionWasOpen = false + + self.appGroupObserver = AppGroupFlagObserver(fileManager: .default, appGroup: Bundle.main.appGroupName) + do { + try appGroupObserver?.prepare() + try appGroupObserver?.removeFlagIfExists(.shareExtension) + try appGroupObserver?.observe { flags in + if !shareExtensionWasOpen, flags.contains(.shareExtension) { + shareExtensionWasOpen = true + MQTTService.sharedInstance.disconnect() + } + } + } catch { + LogService.log(topic: .fileSystem) { error.localizedDescription } + } + let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) - activityViewController.completionWithItemsHandler = { (type, flag, objects, error) in - MQTTService.sharedInstance.reconnect() + activityViewController.completionWithItemsHandler = { [weak self] _, _, _, _ in + + self?.appGroupObserver?.stopObserving() + self?.appGroupObserver = nil + + if shareExtensionWasOpen { + MQTTService.sharedInstance.reconnect() + } } presentingController?.present(activityViewController, animated: true, completion: nil) diff --git a/Nynja/Modules/GroupStorage/View/GroupStorageListVC.swift b/Nynja/Modules/GroupStorage/View/GroupStorageListVC.swift index afbdace84d78b9413c051bf787e2c7cfebc172c8..78d8c57762e992d90a725450a9462e689c672b18 100644 --- a/Nynja/Modules/GroupStorage/View/GroupStorageListVC.swift +++ b/Nynja/Modules/GroupStorage/View/GroupStorageListVC.swift @@ -213,7 +213,6 @@ class GroupStorageListVC : UIViewController, GroupStorageCellDelegate, ContextMe delegate?.pathForUrl(url, completion: { (path) in if path != nil { - MQTTService.sharedInstance.disconnect() let localUrl = URL(fileURLWithPath: path!) AlertManager.sharedInstance.showNativeShare(with: [localUrl]) } diff --git a/Nynja/Modules/InviteFriends/WireFrame/InviteFriendsWireframe.swift b/Nynja/Modules/InviteFriends/WireFrame/InviteFriendsWireframe.swift index 9c05f4b5aa965fc1c4ffbec0822578b7a7f5c482..0908aad2cf066727b9bbcced15c6b25da9f95a29 100644 --- a/Nynja/Modules/InviteFriends/WireFrame/InviteFriendsWireframe.swift +++ b/Nynja/Modules/InviteFriends/WireFrame/InviteFriendsWireframe.swift @@ -58,8 +58,7 @@ class InviteFriendsWireFrame: InviteFriendsWireFrameProtocol { } func shareNynja() { - let activityVC = UIActivityViewController(activityItems: ["nynja_share_string".localized], applicationActivities: nil) - viewController?.present(activityVC, animated: true, completion: nil) + AlertManager.sharedInstance.showNativeShare(with: ["nynja_share_string".localized]) } func closeSMSscreen(smsScreen: MFMessageComposeViewController) { diff --git a/Nynja/Modules/Message/View/MessageVC.swift b/Nynja/Modules/Message/View/MessageVC.swift index c8cc69c0bcc217f32c33465e6ab7e99edb326100..c93c62e8230fd32907a322b6b70a4cb59666ef07 100644 --- a/Nynja/Modules/Message/View/MessageVC.swift +++ b/Nynja/Modules/Message/View/MessageVC.swift @@ -908,7 +908,6 @@ final class MessageVC: BaseVC, MessageViewProtocol, ReplyPreviewDelegate, BackSw } func showNativeShare(for fileUrl: URL) { - MQTTService.sharedInstance.disconnect() AlertManager.sharedInstance.showNativeShare(with: [fileUrl]) }