mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Handle NSItemProvider public.image data types. (#3541)
This commit is contained in:
@@ -6,23 +6,136 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
extension NSItemProvider {
|
||||
struct PreferredContentType {
|
||||
let type: UTType
|
||||
let fileExtension: String
|
||||
}
|
||||
|
||||
func storeData() async -> URL? {
|
||||
guard let contentType = preferredContentType else {
|
||||
MXLog.error("Invalid NSItemProvider: \(self)")
|
||||
return nil
|
||||
}
|
||||
|
||||
if contentType.type.identifier == UTType.image.identifier {
|
||||
return await generateURLForUIImage(contentType)
|
||||
} else {
|
||||
return await generateURLForGenericData(contentType)
|
||||
}
|
||||
}
|
||||
|
||||
private func generateURLForUIImage(_ contentType: PreferredContentType) async -> URL? {
|
||||
guard let uiImage = try? await loadItem(forTypeIdentifier: contentType.type.identifier) as? UIImage else {
|
||||
MXLog.error("Failed casting UIImage, invalid NSItemProvider: \(self)")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let pngData = uiImage.pngData() else {
|
||||
MXLog.error("Failed extracting PNG data out of the UIImage")
|
||||
return nil
|
||||
}
|
||||
|
||||
do {
|
||||
if let suggestedName = suggestedName as? NSString,
|
||||
// Suggestions are nice but their extension is `jpeg`
|
||||
let filename = (suggestedName.deletingPathExtension as NSString).appendingPathExtension(contentType.fileExtension) {
|
||||
return try FileManager.default.writeDataToTemporaryDirectory(data: pngData, fileName: filename)
|
||||
} else {
|
||||
let filename = "\(UUID().uuidString).\(contentType.fileExtension)"
|
||||
return try FileManager.default.writeDataToTemporaryDirectory(data: pngData, fileName: filename)
|
||||
}
|
||||
} catch {
|
||||
MXLog.error("Failed storing NSItemProvider data \(self) with error: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func generateURLForGenericData(_ contentType: PreferredContentType) async -> URL? {
|
||||
let providerDescription = description
|
||||
let shareData: Data? = await withCheckedContinuation { continuation in
|
||||
_ = loadDataRepresentation(for: contentType.type) { data, error in
|
||||
if let error {
|
||||
MXLog.error("Failed processing NSItemProvider: \(providerDescription) with error: \(error)")
|
||||
continuation.resume(returning: nil)
|
||||
return
|
||||
}
|
||||
|
||||
guard let data else {
|
||||
MXLog.error("Invalid NSItemProvider data: \(providerDescription)")
|
||||
continuation.resume(returning: nil)
|
||||
return
|
||||
}
|
||||
|
||||
continuation.resume(returning: data)
|
||||
}
|
||||
}
|
||||
|
||||
guard let shareData else {
|
||||
MXLog.error("Invalid share data")
|
||||
return nil
|
||||
}
|
||||
|
||||
do {
|
||||
if let filename = suggestedName {
|
||||
let hasExtension = !(filename as NSString).pathExtension.isEmpty
|
||||
let filename = hasExtension ? filename : "\(filename).\(contentType.fileExtension)"
|
||||
return try FileManager.default.writeDataToTemporaryDirectory(data: shareData, fileName: filename)
|
||||
} else {
|
||||
let filename = "\(UUID().uuidString).\(contentType.fileExtension)"
|
||||
return try FileManager.default.writeDataToTemporaryDirectory(data: shareData, fileName: filename)
|
||||
}
|
||||
} catch {
|
||||
MXLog.error("Failed storing NSItemProvider data \(self) with error: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var isSupportedForPasteOrDrop: Bool {
|
||||
preferredContentType != nil
|
||||
}
|
||||
|
||||
var preferredContentType: UTType? {
|
||||
var preferredContentType: PreferredContentType? {
|
||||
let supportedContentTypes = registeredContentTypes
|
||||
.filter { isMimeTypeSupported($0.preferredMIMEType) }
|
||||
.filter { isMimeTypeSupported($0.preferredMIMEType) || isIdentifierSupported($0.identifier) }
|
||||
|
||||
// Have .jpeg take priority over .heic
|
||||
if supportedContentTypes.contains(.jpeg) {
|
||||
return .jpeg
|
||||
guard let fileExtension = preferredFileExtension(for: .jpeg) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return .init(type: .jpeg, fileExtension: fileExtension)
|
||||
}
|
||||
|
||||
return supportedContentTypes.first
|
||||
guard let preferredContentType = supportedContentTypes.first,
|
||||
let fileExtension = preferredFileExtension(for: preferredContentType) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return .init(type: preferredContentType, fileExtension: fileExtension)
|
||||
}
|
||||
|
||||
private func preferredFileExtension(for contentType: UTType) -> String? {
|
||||
if let fileExtension = contentType.preferredFilenameExtension {
|
||||
return fileExtension
|
||||
}
|
||||
|
||||
switch contentType.identifier {
|
||||
case UTType.image.identifier:
|
||||
return "png"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func isIdentifierSupported(_ identifier: String?) -> Bool {
|
||||
// Don't filter out generic public.image content as screenshots are in this format
|
||||
// and we can convert them to a PNG ourselves.
|
||||
identifier == UTType.image.identifier
|
||||
}
|
||||
|
||||
private func isMimeTypeSupported(_ mimeType: String?) -> Bool {
|
||||
|
||||
Reference in New Issue
Block a user