Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Threema/ThreemaFramework/Utilities/   (Social Medium ©)  Datei vom 26.9.2026 mit Größe 15 kB image not shown  

Quelle  ItemLoader.swift   Sprache: JAVA

 

import CocoaLumberjackSwift
import FileUtility
import Foundation
import PromiseKit

public final class ItemLoader {
    public enum ItemType: Int {
        case TextOnly
        case Media
    }
    
    public enum TextFileLoaderError: Error {
        case HiddenTextFileError
    }
    
    private enum ItemLoaderError: Error {
        case unknownType
        case loadItemFailed
    }
    
    public var itemsToSend: Set<IntermediateItem> = Set()
    
    private let fileOperationQueue = DispatchQueue(label: "fileOperationQueue", qos: .userInitiated)
    private let forceLoadFileURLItem: Bool
    private let tmpDirURL: URL
    private let imageSender = ImageURLSenderItemCreator()

    public convenience init() {
        self.init(forceLoadFileURLItem: false)
    }
    
    public init(forceLoadFileURLItem: Bool) {
        self.forceLoadFileURLItem = forceLoadFileURLItem
        self.tmpDirURL = ItemLoader.getTemporaryDirectory()
    }
    
    public func syncLoadContentItems() -> [Any]? {
        let sema = DispatchSemaphore(value: 0)
        filterContentItems()
        var finalItems: [Any]?
        _ = loadItems().done(on: DispatchQueue.global(qos: .userInteractive)) { items in
            finalItems = items
            sema.signal()
        }.catch(on: DispatchQueue.global(qos: .userInteractive)) { error in
            DDLogError("Could not load items because \(error)")
            sema.signal()
        }
        // This waits & blocks the main queue, thus the `done` and catch` need to be run on another queue
        sema.wait()
        return finalItems
    }
    
    private static func getTemporaryDirectory() -> URL {
        var url: URL
        var exists = true
        let fileUtility = FileUtility.shared!
        repeat {
            url = fileUtility.appTemporaryUnencryptedDirectory.appendingPathComponent("\(Int.random(in: 0...32768))")
            exists = fileUtility.fileExists(at: url)
        }
        while exists
        do {
            try fileUtility.mkDir(at: url, withIntermediateDirectories: true, attributes: nil)
        }
        catch {
            DDLogError("Error occured while creating temporary directory. Error: \(error)")
        }
        
        return url
    }
    
    public func deleteTempDir() {
        do {
            try FileUtility.shared.delete(at: tmpDirURL)
        }
        catch {
            DDLogError("Could not remove temporary directory!")
        }
    }
    
    public func loadItems() -> Promise<[Any]> {
        var items = [Promise<Any>]()
        for intermediaryItem in itemsToSend {
            items.append(loadItem(intermediaryItem))
        }
        return when(fulfilled: items)
    }
    
    public func checkItemType() -> ItemType {
        for item in itemsToSend {
            if isTextItem(item: item) {
                return .TextOnly
            }
        }
        return .Media
    }
    
    private func isTextItem(item: IntermediateItem) -> Bool {
        let firstType = item.type
        let checkedType = checkSecondaryType(item)
        
        let isText = UTIConverter.type(checkedType, conformsTo: UTType.plainText.identifier)
        let isURL = UTIConverter.type(checkedType, conformsTo: UTType.url.identifier)

        let firstTypeIsFileURL = UTIConverter.type(firstType, conformsTo: UTType.fileURL.identifier)
        let secondTypeIsFileURL = UTIConverter.type(checkedType, conformsTo: UTType.fileURL.identifier)
        let anyTypeIsFileURL = firstTypeIsFileURL || secondTypeIsFileURL
        
        return (isText || isURL) && !anyTypeIsFileURL
    }
    
    private func getTextItems(items: Set<IntermediateItem>) -> Set<IntermediateItem> {
        items.filter { self.isTextItem(item: $0) }
    }
    
    private func getContentItems(items: Set<IntermediateItem>) -> Set<IntermediateItem> {
        items.filter { UTIConverter.type(checkSecondaryType($0), conformsTo: UTType.content.identifier) }
    }
    
    public func filterContentItems() {
        itemsToSend = getContentItems(items: itemsToSend)
    }
    
    public func filterTextItems() {
        itemsToSend = getTextItems(items: itemsToSend)
    }
    
    public func generatePreviewText(items: Promise<[Any]>) -> Promise<(String, NSRange?)> {
        items.then { results -> Promise<(String, NSRange?)> in
            
            if results.count == 1, let firstURL = results.first as? URL, firstURL.isFileURL {
                return .init(error: TextFileLoaderError.HiddenTextFileError)
            }
            
            let sorted = results.sorted { left, _ -> Bool in
                left is URL
            }
            let reversed = self.markTextContent(sorted)
            let text = reversed.text
            let startPosition = reversed.startPosition
            
            var range: NSRange?
            
            if startPosition != text.count {
                range = NSRange(location: startPosition, length: text.count - startPosition)
            }
            
            return .value((text, range))
        }
    }
    
    /// Adds the strings or urls from reversed results
    /// - Parameter results: An array of containing String and/or URL
    /// - Returns: The strings and/or URls in a single String and the start position of the second item; or -1 if there
    ///            is no second item
    public func markTextContent(_ results: [Any]) -> (text: String, startPosition: Int) {
        var text = ""
        var startPosition = -1
        for result in results {
            if result is String {
                text.append(result as! String)
            }
            else if result is URL {
                if (result as! URL).isFileURL {
                    continue
                }
                text.append((result as! URL).absoluteString)
            }
            else {
                let err = "Generating a preview text from an item that is not a string or an url is not allowed"
                DDLogError("\(err)")
                fatalError(err)
            }
            text.append("\n")
            
            if startPosition == -1 {
                startPosition = text.count
            }
        }
        return (text, startPosition)
    }
    
    public func addItem(itemProvider: NSItemProvider, type: String, secondType: String?) {
        let item = IntermediateItem(itemProvider: itemProvider, type: type, secondType: secondType, caption: nil)
        itemsToSend.insert(item)
    }
    
    private func copyItem(at: URL, to: URL) -> (Bool, Error?) {
        do {
            try FileUtility.shared.copy(from: at, to: to)
        }
        catch {
            return (false, error)
        }
        return (true, nil)
    }
    
    private func checkSecondaryType(_ intermediateItem: IntermediateItem) -> String {
        let chooseSecondType = [
            "com.apple.live-photo",
            "com.apple.avfoundation.urlasset",
            "com.apple.mobileslideshow.asset.localidentifier",
            "com.compuserve.gif",
            "public.file-url",
            "public.url",
            "com.apple.is-remote-clipboard",
            // Copied to clipboard with shortcuts automation
            "link.contentkit.cachingidentifier",
        ]
        
        if chooseSecondType.contains(where: { $0 == intermediateItem.type }),
           let secondType = intermediateItem.secondType {
            return secondType
        }
        
        return intermediateItem.type
    }
    
    private func loadItem(_ intermediateItem: IntermediateItem) -> Promise<Any> {
        let type = checkSecondaryType(intermediateItem)
        if checkItemType() == .Media {
            return Promise { seal in
                let isFileItem = intermediateItem.itemProvider
                    .hasItemConformingToTypeIdentifier(UTType.fileURL.identifier)
                let isContent = intermediateItem.itemProvider
                    .hasItemConformingToTypeIdentifier(UTType.content.identifier)
                if (isFileItem && isContent) || forceLoadFileURLItem {
                    intermediateItem.itemProvider.loadFileRepresentation(forTypeIdentifier: type) { item, error in
                        if let error {
                            DDLogError("loadFileRepresentation failed for type \(type): \(error)")
                            seal.reject(error)
                            return
                        }
                        if let urlItem = item, urlItem.scheme == "file" {
                            self.loadURLItem(urlItem: urlItem, type: type, seal: seal)
                        }
                        else {
                            DDLogError("loadFileRepresentation returned no usable file URL for type \(type)")
                            seal.reject(ItemLoaderError.loadItemFailed)
                        }
                    }
                }
                else {
                    self.loadGeneralURLItem(intermediateItem: intermediateItem, type: type, seal: seal)
                }
            }
        }
        return loadGeneralURLItem(intermediateItem)
    }
    
    private func loadGeneralURLItem(_ intermediateItem: IntermediateItem) -> Promise<Any> {
        let type = checkSecondaryType(intermediateItem)
        return Promise { seal in
            loadGeneralURLItem(intermediateItem: intermediateItem, type: type, seal: seal)
        }
    }
    
    private func loadGeneralURLItem(intermediateItem: IntermediateItem, type: String, seal: Resolver<Any>) {
        intermediateItem.itemProvider.loadItem(
            forTypeIdentifier: type,
            options: nil,
            completionHandler: { item, error in
                if let error {
                    DDLogError("loadItem failed for type \(type): \(error)")
                    seal.reject(error)
                    return
                }
                if let urlItem = item as? URL, urlItem.scheme == "file" {
                    self.loadURLItem(urlItem: urlItem, type: type, seal: seal)
                }
                else if self.isDataLoadable(type: type), let dataItem = item as? Data {
                    self.loadDataItem(dataItem: dataItem, type: type, seal: seal)
                }
                else if UTIConverter.type(type, conformsTo: UTType.image.identifier),
                        let imageItem = item as? UIImage {
                    self.loadImageItem(imageItem: imageItem, seal: seal)
                }
                else if let item {
                    DDLogError("Unsupported item type \(Swift.type(of: item)) for type \(type)")
                    seal.fulfill(item)
                }
                else {
                    DDLogError("Item could not be loaded for type \(type)")
                    seal.reject(ItemLoaderError.unknownType)
                }
            }
        )
    }
    
    private func isDataLoadable(type: String) -> Bool {
        UTIConverter.type(type, conformsTo: UTType.data.identifier) ||
            UTIConverter.isPassMimeType(UTIConverter.mimeType(fromUTI: type) ?? "application/octet-stream") ||
            UTType(type)?.preferredFilenameExtension != nil
    }
    
    private func loadURLItem(urlItem: URL, type: String, seal: Resolver<Any>) {
        let url = tmpDirURL
        let mimeType = UTIConverter.mimeType(fromUTI: type) ?? "application/octet-stream"
        let ext = UTIConverter.preferredFileExtension(forMimeType: mimeType) ?? urlItem.pathExtension
        fileOperationQueue.sync {
            let ogFilename = urlItem.deletingPathExtension().lastPathComponent
            let filename = FileUtility.shared.getUniqueFilename(
                from: ogFilename,
                directoryURL: url,
                pathExtension: ext
            )
            let fileURL = url.appendingPathComponent(filename).appendingPathExtension(ext)
            do {
                try FileUtility.shared.copy(from: urlItem, to: fileURL)
            }
            catch {
                seal.reject(error)
                return
            }
            
            seal.fulfill(fileURL)
        }
    }
    
    private func loadDataItem(dataItem: Data, type: String, seal: Resolver<Any>) {
        let url = tmpDirURL
        fileOperationQueue.sync {
            let mimeType = UTIConverter.mimeType(fromUTI: type) ?? "application/octet-stream"
            guard let ext = UTIConverter.preferredFileExtension(
                forMimeType: mimeType
            ) ?? UTType(type)?.preferredFilenameExtension else {
                DDLogError("Could not determine file extension for type \(type) (MIME: \(mimeType))")
                seal.reject(ItemLoaderError.unknownType)
                return
            }
            let fileUtility = FileUtility.shared!
            let filename = fileUtility.getTemporarySendableFileName(
                base: "file",
                directoryURL: url,
                pathExtension: ext
            )
            let fileURL = url.appendingPathComponent(filename).appendingPathExtension(ext)
            
            guard fileUtility.write(contents: dataItem, to: fileURL) else {
                seal.reject(ItemLoaderError.loadItemFailed)
                return
            }
            
            seal.fulfill(fileURL)
        }
    }
    
    private func loadImageItem(imageItem: UIImage, seal: Resolver<Any>) {
        let isSticker = imageSender.isPNGSticker(image: imageItem, uti: UTType.png.identifier)
        let imageData: Data? =
            if isSticker {
                MediaConverter.pngRepresentation(for: imageItem)
            }
            else {
                MediaConverter.jpegRepresentation(
                    for: imageItem,
                    withQuality: imageSender.imageCompressionQuality()
                )
            }
        guard let dataItem = imageData else {
            let errorDescription = "Could not create jpeg representation of image item"
            DDLogError("\(errorDescription)")
            let errorTemp = NSError(domain: errorDescription, code: 0, userInfo: nil)
            seal.reject(errorTemp)
            return
        }
        
        let url = tmpDirURL
        fileOperationQueue.sync {
            guard
                let fileUtility = FileUtility.shared,
                let jpegExt = UTType.jpeg.preferredFilenameExtension,
                let pngExt = UTType.png.preferredFilenameExtension
            else {
                return
            }

            let ext: String = isSticker ? pngExt : jpegExt

            let filename = fileUtility.getTemporarySendableFileName(
                base: "image",
                directoryURL: url,
                pathExtension: ext
            )
            let fileURL = url.appendingPathComponent(filename).appendingPathExtension(ext)
            
            guard fileUtility.write(contents: dataItem, to: fileURL) else {
                seal.reject(ItemLoaderError.loadItemFailed)
                return
            }
            
            seal.fulfill(fileURL)
        }
    }
    
    public static func getBaseUTIType(_ itemProvider: NSItemProvider) -> String {
        let typeIdentifiers = NSMutableArray(array: itemProvider.registeredTypeIdentifiers)
        
        if typeIdentifiers.count >= 1 {
            return typeIdentifiers.lastObject as! String
        }
        return UTType.fileURL.identifier
    }
    
    public static func getSecondUTIType(_ itemProvider: NSItemProvider, baseType: String) -> String? {
        let typeIdentifiers = NSMutableArray(array: itemProvider.registeredTypeIdentifiers)
        
        if itemProvider.hasItemConformingToTypeIdentifier(UTType.fileURL.identifier) {
            typeIdentifiers.remove(UTType.fileURL.identifier)
        }
        if typeIdentifiers.count >= 1 {
            return (typeIdentifiers.first(where: { element in
                (element as? String)?.contains("public.") ?? false && (element as? String) != baseType
            }) as? String) ?? typeIdentifiers.firstObject as? String
        }
        return UTType.fileURL.identifier
    }
}

Messung
CHZG
869410093

¤ Dauer der Verarbeitung: 0.10 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.