Browse Source

share compressed images via DC

cyberta 5 years ago
parent
commit
73372cc64c

+ 12 - 0
DcCore/DcCore/Extensions/UIImage+Extensions.swift

@@ -37,6 +37,18 @@ public extension UIImage {
         return newImage
     }
 
+    public func dcCompress(toMax target: Float = 1280) -> UIImage? {
+        return scaleDownAndCompress(toMax: target)
+    }
+
+    public func imageSizeInPixel() -> CGSize {
+        let heightInPoints = size.height
+        let heightInPixels = heightInPoints * scale
+        let widthInPoints = size.width
+        let widthInPixels = widthInPoints * scale
+        return CGSize(width: widthInPixels, height: heightInPixels)
+    }
+    
     // if an image has an alpha channel we try to keep it, using PNG formatting instead of JPEG
     // PNGs are less compressed than JPEGs - to keep the message sizes small,
     // the size of PNG imgaes will be scaled down

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

@@ -59,4 +59,26 @@ public struct DcUtils {
         return acc
     }
 
+    // compression needs to be done before in UIImage.dcCompress()
+    public static func saveImage(image: UIImage) -> String? {
+        guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask,
+                                                           appropriateFor: nil, create: false) as NSURL else {
+                                                            return nil
+        }
+
+        guard let data = image.isTransparent() ? image.pngData() : image.jpegData(compressionQuality: 1.0) else {
+            return nil
+        }
+
+        do {
+            let timestamp = Double(Date().timeIntervalSince1970)
+            let path = directory.appendingPathComponent("\(timestamp).jpg")
+            try data.write(to: path!)
+            return path?.relativePath
+        } catch {
+            DcContext.shared.logger?.info(error.localizedDescription)
+            return nil
+        }
+    }
+
 }

+ 6 - 4
DcShare/SendingController.swift

@@ -7,7 +7,7 @@ protocol SendingControllerDelegate: class {
 
 class SendingController: UIViewController {
 
-    private let dcMsg: DcMsg
+    private let dcMsgs: [DcMsg]
     private let chatId: Int
     private let dcContext: DcContext
     weak var delegate: SendingControllerDelegate?
@@ -33,9 +33,9 @@ class SendingController: UIViewController {
         return view
     }()
 
-    init(chatId: Int, dcMsg: DcMsg, dcContext: DcContext) {
+    init(chatId: Int, dcMsgs: [DcMsg], dcContext: DcContext) {
         self.chatId = chatId
-        self.dcMsg = dcMsg
+        self.dcMsgs = dcMsgs
         self.dcContext = dcContext
         super.init(nibName: nil, bundle: nil)
     }
@@ -70,7 +70,9 @@ class SendingController: UIViewController {
 
     private func sendMessage() {
         DispatchQueue.global(qos: .utility).async {
-            self.dcMsg.sendInChat(id: self.chatId)
+            for message in self.dcMsgs {
+                message.sendInChat(id: self.chatId)
+            }
             self.dcContext.performSmtpJobs()
             self.delegate?.onSendingAttemptFinished()
         }

+ 73 - 0
DcShare/ShareAttachment.swift

@@ -0,0 +1,73 @@
+import Foundation
+import MobileCoreServices
+
+class ShareAttachment {
+    enum AttachmentType {
+        case image
+        case video
+        case audio
+        case file
+        case text
+    }
+
+    var inputItems: [Any]?
+
+    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
+    }()
+
+    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
+    }()
+
+    lazy var video: [NSItemProvider] = {
+        return attachments[.video] ?? []
+    }()
+
+    lazy var audio: [NSItemProvider] = {
+        return attachments[.audio] ?? []
+    }()
+
+    lazy var image: [NSItemProvider] = {
+        return attachments[.image] ?? []
+    }()
+
+    lazy var file: [NSItemProvider] = {
+        return attachments[.file] ?? []
+    }()
+
+    lazy var text: [NSItemProvider] = {
+        return attachments[.text] ?? []
+    }()
+
+
+    init(inputItems: [Any]?) {
+        self.inputItems = inputItems
+    }
+
+}

+ 46 - 5
DcShare/ShareViewController.swift

@@ -1,6 +1,7 @@
 import UIKit
 import Social
 import DcCore
+import MobileCoreServices
 
 
 class ShareViewController: SLComposeServiceViewController {
@@ -32,6 +33,11 @@ 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] = []
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -44,6 +50,7 @@ class ShareViewController: SLComposeServiceViewController {
                 }
             }
         }
+        createMessages()
     }
 
     override func presentationAnimationDidFinish() {
@@ -62,7 +69,7 @@ class ShareViewController: SLComposeServiceViewController {
 
     override func isContentValid() -> Bool {
         // Do validation of contentText and/or NSExtensionContext attachments here
-        return  !(contentText?.isEmpty ?? true)
+        return  !(contentText?.isEmpty ?? true) || !self.shareAttachment.isEmpty
     }
 
     private func setupNavigationBar() {
@@ -80,14 +87,48 @@ class ShareViewController: SLComposeServiceViewController {
     @objc
     private func appendPostTapped() {
         if let chatId = self.selectedChatId {
-            let message = DcMsg(viewType: DC_MSG_TEXT)
-            message.text = self.contentText
-            let chatListController = SendingController(chatId: chatId, dcMsg: message, dcContext: dcContext)
+            if !self.contentText.isEmpty {
+                if messages.count == 1 {
+                    messages[0].text?.append(self.contentText)
+                } else {
+                    let message = DcMsg(viewType: DC_MSG_TEXT)
+                    message.text = self.contentText
+                    messages.insert(message, at: 0)
+                }
+            }
+            let chatListController = SendingController(chatId: chatId, dcMsgs: messages, dcContext: dcContext)
             chatListController.delegate = self
             self.pushConfigurationViewController(chatListController)
         }
     }
 
+    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()
@@ -98,7 +139,7 @@ class ShareViewController: SLComposeServiceViewController {
     }
 
     override func configurationItems() -> [Any]! {
-         logger.debug("configurationItems")
+        logger.debug("configurationItems")
         // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
 
         let item = SLComposeSheetConfigurationItem()

+ 28 - 0
deltachat-ios.xcodeproj/project.pbxproj

@@ -12,6 +12,7 @@
 		30149D9322F21129003C12B5 /* QrViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30149D9222F21129003C12B5 /* QrViewController.swift */; };
 		3015634423A003BA00E9DEF4 /* AudioRecorderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3015634323A003BA00E9DEF4 /* AudioRecorderController.swift */; };
 		3022E6BE22E8768800763272 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3022E6C022E8768800763272 /* InfoPlist.strings */; };
+		302589FF2452FA280086C1CD /* ShareAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302589FE2452FA280086C1CD /* ShareAttachment.swift */; };
 		30260CA7238F02F700D8D52C /* MultilineTextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30260CA6238F02F700D8D52C /* MultilineTextFieldCell.swift */; };
 		302B84C6239676F0001C261F /* AvatarHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AC265E237F1807002A943F /* AvatarHelper.swift */; };
 		302B84C72396770B001C261F /* RelayHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B84C42396627F001C261F /* RelayHelper.swift */; };
@@ -253,6 +254,7 @@
 		3022E6D122E8769E00763272 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		3022E6D222E8769F00763272 /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		3022E6D322E876A100763272 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		302589FE2452FA280086C1CD /* ShareAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAttachment.swift; sourceTree = "<group>"; };
 		30260CA6238F02F700D8D52C /* MultilineTextFieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineTextFieldCell.swift; sourceTree = "<group>"; };
 		302B84C42396627F001C261F /* RelayHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayHelper.swift; sourceTree = "<group>"; };
 		302B84CD2397F6CD001C261F /* URL+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extension.swift"; sourceTree = "<group>"; };
@@ -532,6 +534,27 @@
 			path = DC;
 			sourceTree = "<group>";
 		};
+		304F5E452451D27500462538 /* Extensions */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = Extensions;
+			sourceTree = "<group>";
+		};
+		304F5E462451D2AA00462538 /* Controller */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = Controller;
+			sourceTree = "<group>";
+		};
+		304F5E472451D2CA00462538 /* View */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		3059617E234610A800C80F33 /* MessageKit */ = {
 			isa = PBXGroup;
 			children = (
@@ -687,6 +710,9 @@
 		30E8F2112447285600CE2C90 /* DcShare */ = {
 			isa = PBXGroup;
 			children = (
+				304F5E472451D2CA00462538 /* View */,
+				304F5E462451D2AA00462538 /* Controller */,
+				304F5E452451D27500462538 /* Extensions */,
 				30E8F21F24472AAE00CE2C90 /* DcShare.entitlements */,
 				30E8F2122447285600CE2C90 /* ShareViewController.swift */,
 				30E8F2412448B77600CE2C90 /* ChatListController.swift */,
@@ -694,6 +720,7 @@
 				30E8F2172447285600CE2C90 /* Info.plist */,
 				30E8F2432449C64100CE2C90 /* ChatListCell.swift */,
 				30E8F252244DAD0E00CE2C90 /* SendingController.swift */,
+				302589FE2452FA280086C1CD /* ShareAttachment.swift */,
 			);
 			path = DcShare;
 			sourceTree = "<group>";
@@ -1252,6 +1279,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				302589FF2452FA280086C1CD /* ShareAttachment.swift in Sources */,
 				30E8F2442449C64100CE2C90 /* ChatListCell.swift in Sources */,
 				30E8F2132447285600CE2C90 /* ShareViewController.swift in Sources */,
 				30E8F253244DAD0E00CE2C90 /* SendingController.swift in Sources */,

+ 1 - 1
deltachat-ios/Controller/ChatViewController.swift

@@ -974,7 +974,7 @@ extension ChatViewController: MessagesDataSource {
             if let compressedImage = image.dcCompress() {
                 // at this point image is compressed by 85% by default
                 let pixelSize = compressedImage.imageSizeInPixel()
-                let path = Utils.saveImage(image: compressedImage)
+                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)

+ 0 - 13
deltachat-ios/Extensions/UIImage+Extension.swift

@@ -24,19 +24,6 @@ extension UIImage {
         self.init(cgImage: cgImage)
     }
 
-
-    func dcCompress(toMax target: Float = 1280) -> UIImage? {
-        return scaleDownAndCompress(toMax: target)
-    }
-
-    func imageSizeInPixel() -> CGSize {
-        let heightInPoints = size.height
-        let heightInPixels = heightInPoints * scale
-        let widthInPoints = size.width
-        let widthInPixels = widthInPoints * scale
-        return CGSize(width: widthInPixels, height: heightInPixels)
-    }
-
 }
 
 public enum ImageType: String {

+ 0 - 22
deltachat-ios/Helper/Utils.swift

@@ -53,28 +53,6 @@ struct Utils {
         return addressParts.joined(separator: ", ")
     }
 
-    // compression needs to be done before in UIImage.dcCompress()
-    static func saveImage(image: UIImage) -> String? {
-        guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask,
-            appropriateFor: nil, create: false) as NSURL else {
-            return nil
-        }
-
-        guard let data = image.isTransparent() ? image.pngData() : image.jpegData(compressionQuality: 1.0) else {
-            return nil
-        }
-
-        do {
-            let timestamp = Double(Date().timeIntervalSince1970)
-            let path = directory.appendingPathComponent("\(timestamp).jpg")
-            try data.write(to: path!)
-            return path?.relativePath
-        } catch {
-            logger.info(error.localizedDescription)
-            return nil
-        }
-    }
-
     static func hasAudioSuffix(url: URL) -> Bool {
         ///TODO: add more file suffixes
         return url.absoluteString.hasSuffix("wav")