瀏覽代碼

refactoring + support for file, audio and video sharing

cyberta 5 年之前
父節點
當前提交
070e4c1447
共有 4 個文件被更改,包括 132 次插入99 次删除
  1. 13 0
      DcCore/DcCore/Helper/DcUtils.swift
  2. 12 10
      DcShare/Info.plist
  3. 92 55
      DcShare/ShareAttachment.swift
  4. 15 34
      DcShare/ShareViewController.swift

+ 13 - 0
DcCore/DcCore/Helper/DcUtils.swift

@@ -1,5 +1,6 @@
 import Foundation
 import UIKit
+import MobileCoreServices
 
 public struct DcUtils {
 
@@ -81,4 +82,16 @@ public struct DcUtils {
         }
     }
 
+    public static func getMimeTypeForPath(path: String) -> String {
+        let url = NSURL(fileURLWithPath: path)
+        let pathExtension = url.pathExtension
+
+        if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension! as NSString, nil)?.takeRetainedValue() {
+            if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
+                return mimetype as String
+            }
+        }
+        return "application/octet-stream"
+    }
+
 }

+ 12 - 10
DcShare/Info.plist

@@ -25,16 +25,18 @@
 		<key>NSExtensionAttributes</key>
 		<dict>
 			<key>NSExtensionActivationRule</key>
-			<dict>
-				<key>NSExtensionActivationDictionaryVersion</key>
-				<integer>2</integer>
-				<key>NSExtensionActivationSupportsImageWithMaxCount</key>
-				<integer>10</integer>
-				<key>NSExtensionActivationSupportsText</key>
-				<true/>
-				<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
-				<string>10</string>
-			</dict>
+			<string>SUBQUERY (
+extensionItems,
+$extensionItem,
+SUBQUERY (
+$extensionItem.attachments,
+$attachment,
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" ||
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.audio" ||
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" ||
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
+).@count == $extensionItem.attachments.@count
+).@count >= 1</string>
 		</dict>
 		<key>NSExtensionMainStoryboard</key>
 		<string>MainInterface</string>

+ 92 - 55
DcShare/ShareAttachment.swift

@@ -1,73 +1,110 @@
 import Foundation
 import MobileCoreServices
+import DcCore
+import UIKit
 
+protocol ShareAttachmentDelegate: class {
+    func onAttachmentAdded()
+}
 class ShareAttachment {
-    enum AttachmentType {
-        case image
-        case video
-        case audio
-        case file
-        case text
-    }
+
+    weak var delegate: ShareAttachmentDelegate?
+    let dcContext: DcContext
 
     var inputItems: [Any]?
+    var messages: [DcMsg] = []
 
-    lazy var attachments: [AttachmentType: [NSItemProvider]] = {
-       var attachments: [AttachmentType: [NSItemProvider]] = [
-           .image: [],
-           .video: [],
-           .audio: [],
-           .file: [],
-           .text: []]
-       guard let items = inputItems as? [NSExtensionItem] else { return [:] }
-       let flatArray = items.flatMap { $0.attachments ?? [] }
-       for item in flatArray {
-        if item.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
-               attachments[.image]?.append(item)
-        } else if item.hasItemConformingToTypeIdentifier(kUTTypeMovie as String) {
-               attachments[.video]?.append(item)
-           } else if item.hasItemConformingToTypeIdentifier(kUTTypeAudio as String) {
-               attachments[.audio]?.append(item)
-           } else if item.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
-               attachments[.text]?.append(item)
-           } else if item.hasItemConformingToTypeIdentifier(kUTTypeContent as String) {
-               attachments[.file]?.append(item)
-           }
-       }
-       return attachments
-    }()
+    var isEmpty: Bool {
+        return messages.isEmpty
+    }
 
-    lazy var isEmpty: Bool = {
-        return attachments[.image]?.isEmpty ?? true &&
-            attachments[.video]?.isEmpty ?? true &&
-            attachments[.audio]?.isEmpty ?? true &&
-            attachments[.file]?.isEmpty ?? true &&
-            attachments[.text]?.isEmpty ?? true
-    }()
+    init(dcContext: DcContext, inputItems: [Any]?, delegate: ShareAttachmentDelegate) {
+        self.dcContext = dcContext
+        self.inputItems = inputItems
+        self.delegate = delegate
+        createMessages()
+    }
 
-    lazy var video: [NSItemProvider] = {
-        return attachments[.video] ?? []
-    }()
 
-    lazy var audio: [NSItemProvider] = {
-        return attachments[.audio] ?? []
-    }()
+    func createMessages() {
+        guard let items = inputItems as? [NSExtensionItem] else { return }
+        for item in items {
+            if let attachments = item.attachments {
+                createMessageFromDataRepresentaion(attachments)
+            }
+        }
+    }
 
-    lazy var image: [NSItemProvider] = {
-        return attachments[.image] ?? []
-    }()
+    // a NSExtensionItem can have multiple attachments representing the same data in diffent types
+    // we want only one DcMsg per NSExtensionItem which is why we're breaking out of the loop
+    // after the first match
+    func createMessageFromDataRepresentaion(_ attachments: [NSItemProvider]) {
+        for attachment in attachments {
+            if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
+                createImageMsg(attachment)
+                break
+            } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeMovie as String) {
+                createMoviewMsg(attachment)
+                break
+            } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeAudio as String) {
+                createAudioMsg(attachment)
+                break
+            } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeFileURL as String) {
+                createFileMsg(attachment)
+                break
+            }
+        }
+    }
 
-    lazy var file: [NSItemProvider] = {
-        return attachments[.file] ?? []
-    }()
+    func createImageMsg(_ item: NSItemProvider) {
+        item.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { data, error in
+            let result: UIImage?
+            switch data {
+            case let image as UIImage:
+                result = image
+            case let data as Data:
+                result = UIImage(data: data)
+            case let url as URL:
+                result = UIImage(contentsOfFile: url.path)
+            default:
+                self.dcContext.logger?.debug("Unexpected data: \(type(of: data))")
+                result = nil
+            }
+            if let result = result, let compressedImage = result.dcCompress() {
+                let pixelSize = compressedImage.imageSizeInPixel()
+                let path = DcUtils.saveImage(image: compressedImage)
+                let msg = DcMsg(viewType: DC_MSG_IMAGE)
+                msg.setFile(filepath: path, mimeType: "image/jpeg")
+                msg.setDimension(width: pixelSize.width, height: pixelSize.height)
+                self.messages.append(msg)
+                self.delegate?.onAttachmentAdded()
+            }
+        }
+    }
 
-    lazy var text: [NSItemProvider] = {
-        return attachments[.text] ?? []
-    }()
+    func createMoviewMsg(_ item: NSItemProvider) {
+         createDcMsgFromURL(item: item, typeIdentifier: kUTTypeMovie, viewType: DC_MSG_VIDEO)
+    }
 
+    func createAudioMsg(_ item: NSItemProvider) {
+        createDcMsgFromURL(item: item, typeIdentifier: kUTTypeAudio, viewType: DC_MSG_AUDIO)
+    }
 
-    init(inputItems: [Any]?) {
-        self.inputItems = inputItems
+    func createFileMsg(_ item: NSItemProvider) {
+        createDcMsgFromURL(item: item, typeIdentifier: kUTTypeFileURL, viewType: DC_MSG_FILE)
     }
 
+    func createDcMsgFromURL(item: NSItemProvider, typeIdentifier: CFString, viewType: Int32) {
+        item.loadItem(forTypeIdentifier: typeIdentifier as String, options: nil) { data, error in
+            switch data {
+            case let url as URL:
+                let msg = DcMsg(viewType: viewType)
+                msg.setFile(filepath: url.path, mimeType: DcUtils.getMimeTypeForPath(path: url.path))
+                self.messages.append(msg)
+                self.delegate?.onAttachmentAdded()
+            default:
+                self.dcContext.logger?.debug("Unexpected data: \(type(of: data))")
+            }
+        }
+    }
 }

+ 15 - 34
DcShare/ShareViewController.swift

@@ -33,11 +33,7 @@ class ShareViewController: SLComposeServiceViewController {
     var selectedChatId: Int?
     var selectedChat: DcChat?
     let dbHelper = DatabaseHelper()
-    lazy var shareAttachment: ShareAttachment = {
-        return ShareAttachment(inputItems: self.extensionContext?.inputItems)
-    }()
-
-    var messages: [DcMsg] = []
+    var shareAttachment: ShareAttachment?
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -50,7 +46,10 @@ class ShareViewController: SLComposeServiceViewController {
                 }
             }
         }
-        createMessages()
+
+        DispatchQueue.global(qos: .background).async {
+            self.shareAttachment = ShareAttachment(dcContext: self.dcContext, inputItems: self.extensionContext?.inputItems, delegate: self)
+        }
     }
 
     override func presentationAnimationDidFinish() {
@@ -69,7 +68,7 @@ class ShareViewController: SLComposeServiceViewController {
 
     override func isContentValid() -> Bool {
         // Do validation of contentText and/or NSExtensionContext attachments here
-        return  !(contentText?.isEmpty ?? true) || !self.shareAttachment.isEmpty
+        return  !(contentText?.isEmpty ?? true) || !(self.shareAttachment?.isEmpty ?? true)
     }
 
     private func setupNavigationBar() {
@@ -87,6 +86,7 @@ class ShareViewController: SLComposeServiceViewController {
     @objc
     private func appendPostTapped() {
         if let chatId = self.selectedChatId {
+            guard var messages = shareAttachment?.messages else { return }
             if !self.contentText.isEmpty {
                 if messages.count == 1 {
                     messages[0].text?.append(self.contentText)
@@ -102,33 +102,6 @@ class ShareViewController: SLComposeServiceViewController {
         }
     }
 
-    private func createMessages() {
-        for item in shareAttachment.image {
-            item.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { data, error in
-                let result: UIImage?
-                switch data {
-                case let image as UIImage:
-                    result = image
-                case let data as Data:
-                    result = UIImage(data: data)
-                case let url as URL:
-                    result = UIImage(contentsOfFile: url.path)
-                default:
-                    self.dcContext.logger?.debug("Unexpected data: \(type(of: data))")
-                    result = nil
-                }
-                if let result = result, let compressedImage = result.dcCompress() {
-                    let pixelSize = compressedImage.imageSizeInPixel()
-                    let path = DcUtils.saveImage(image: compressedImage)
-                    let msg = DcMsg(viewType: DC_MSG_IMAGE)
-                    msg.setFile(filepath: path, mimeType: "image/jpeg")
-                    msg.setDimension(width: pixelSize.width, height: pixelSize.height)
-                    self.messages.append(msg)
-                }
-            }
-        }
-    }
-
     func quit() {
         if dbHelper.currentDatabaseLocation == dbHelper.sharedDbFile {
             dcContext.closeDatabase()
@@ -176,3 +149,11 @@ extension ShareViewController: SendingControllerDelegate {
         }
     }
 }
+
+extension ShareViewController: ShareAttachmentDelegate {
+    func onAttachmentAdded() {
+        DispatchQueue.main.async {
+            self.validateContent()
+        }
+    }
+}