Swiftgram/TelegramUI/LegacyMediaPickers.swift
2018-07-21 21:31:48 +03:00

436 lines
28 KiB
Swift

import Foundation
import LegacyComponents
import SwiftSignalKit
import TelegramCore
import Postbox
import SSignalKit
import UIKit
import Display
import TelegramUIPrivateModule
func guessMimeTypeByFileExtension(_ ext: String) -> String {
return TGMimeTypeMap.mimeType(forExtension: ext) ?? "application/binary"
}
func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, account: Account, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false) {
controller.captionsEnabled = captionsEnabled
controller.inhibitDocumentCaptions = false
controller.suggestionContext = legacySuggestionContext(account: account, peerId: peer.id)
if (peer is TelegramUser || peer is TelegramSecretChat) && peer.id != account.peerId {
controller.hasTimer = true
}
controller.dismissalBlock = {
}
controller.localMediaCacheEnabled = false
controller.shouldStoreAssets = storeCreatedAssets
controller.shouldShowFileTipIfNeeded = showFileTooltip
}
func legacyAssetPicker(applicationContext: TelegramApplicationContext, presentationData: PresentationData, editingMedia: Bool, fileMode: Bool, peer: Peer?, saveEditedPhotos: Bool, allowGrouping: Bool) -> Signal<(LegacyComponentsContext) -> TGMediaAssetsController, NoError> {
return Signal { subscriber in
let intent = fileMode ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent
authorizeDeviceAccess(to: .mediaLibrary(.send), presentationData: presentationData, present: applicationContext.presentGlobalController, openSettings: applicationContext.applicationBindings.openSettings, { value in
if !value {
subscriber.putError(NoError())
return
}
if TGMediaAssetsLibrary.authorizationStatus() == TGMediaLibraryAuthorizationStatusNotDetermined {
TGMediaAssetsLibrary.requestAuthorization(for: TGMediaAssetAnyType, completion: { (status, group) in
if !LegacyComponentsGlobals.provider().accessChecker().checkPhotoAuthorizationStatus(for: TGPhotoAccessIntentRead, alertDismissCompletion: nil) {
subscriber.putError(NoError())
} else {
Queue.mainQueue().async {
subscriber.putNext({ context in
let controller = TGMediaAssetsController(context: context, assetGroup: group, intent: intent, recipientName: peer?.displayTitle, saveEditedPhotos: saveEditedPhotos, allowGrouping: allowGrouping, inhibitSelection: editingMedia)
return controller!
})
subscriber.putCompletion()
}
}
})
} else {
subscriber.putNext({ context in
let controller = TGMediaAssetsController(context: context, assetGroup: nil, intent: intent, recipientName: peer?.displayTitle, saveEditedPhotos: saveEditedPhotos, allowGrouping: allowGrouping)
return controller!
})
subscriber.putCompletion()
}
})
return ActionDisposable {
}
}
}
private enum LegacyAssetImageData {
case image(UIImage)
case asset(PHAsset)
case tempFile(String)
}
private enum LegacyAssetVideoData {
case asset(TGMediaAsset)
case tempFile(path: String, dimensions: CGSize, duration: Double)
}
private enum LegacyAssetItem {
case image(data: LegacyAssetImageData, caption: String?)
case file(data: LegacyAssetImageData, mimeType: String, name: String, caption: String?)
case video(data: LegacyAssetVideoData, previewImage: UIImage?, adjustments: TGVideoEditAdjustments?, caption: String?, asFile: Bool, asAnimation: Bool)
}
private final class LegacyAssetItemWrapper: NSObject {
let item: LegacyAssetItem
let timer: Int?
let groupedId: Int64?
init(item: LegacyAssetItem, timer: Int?, groupedId: Int64?) {
self.item = item
self.timer = timer
self.groupedId = groupedId
super.init()
}
}
func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?) -> [AnyHashable : Any]?) {
return { anyDict, caption, entities, hash in
let dict = anyDict as! NSDictionary
if (dict["type"] as! NSString) == "editedPhoto" || (dict["type"] as! NSString) == "capturedPhoto" {
let image = dict["image"] as! UIImage
var result: [AnyHashable : Any] = [:]
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .image(image), caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
return result
} else if (dict["type"] as! NSString) == "cloudPhoto" {
let asset = dict["asset"] as! TGMediaAsset
var asFile = false
if let document = dict["document"] as? NSNumber, document.boolValue {
asFile = true
}
var result: [AnyHashable: Any] = [:]
if asFile {
var mimeType = "image/jpeg"
if let customMimeType = dict["mimeType"] as? String {
mimeType = customMimeType
}
var name = "image.jpg"
if let customName = dict["fileName"] as? String {
name = customName
}
result["item" as NSString] = LegacyAssetItemWrapper(item: .file(data: .asset(asset.backingAsset), mimeType: mimeType, name: name, caption: caption), timer: nil, groupedId: nil)
} else {
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .asset(asset.backingAsset), caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
}
return result
} else if (dict["type"] as! NSString) == "file" {
if let tempFileUrl = dict["tempFileUrl"] as? URL {
var mimeType = "application/binary"
if let customMimeType = dict["mimeType"] as? String {
mimeType = customMimeType
}
var name = "file"
if let customName = dict["fileName"] as? String {
name = customName
}
if let isAnimation = dict["isAnimation"] as? NSNumber, isAnimation.boolValue, mimeType == "video/mp4" {
var result: [AnyHashable: Any] = [:]
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
let duration = (dict["duration"]! as AnyObject).doubleValue!
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: tempFileUrl.path, dimensions: dimensions, duration: duration), previewImage: dict["previewImage"] as? UIImage, adjustments: nil, caption: caption, asFile: false, asAnimation: true), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
return result
}
var result: [AnyHashable: Any] = [:]
result["item" as NSString] = LegacyAssetItemWrapper(item: .file(data: .tempFile(tempFileUrl.path), mimeType: mimeType, name: name, caption: caption), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
return result
}
} else if (dict["type"] as! NSString) == "video" {
var asFile = false
if let document = dict["document"] as? NSNumber, document.boolValue {
asFile = true
}
if let asset = dict["asset"] as? TGMediaAsset {
var result: [AnyHashable: Any] = [:]
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .asset(asset), previewImage: dict["previewImage"] as? UIImage, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
return result
} else if let url = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString {
let dimensions = (dict["dimensions"]! as AnyObject).cgSizeValue!
let duration = (dict["duration"]! as AnyObject).doubleValue!
var result: [AnyHashable: Any] = [:]
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), previewImage: dict["previewImage"] as? UIImage, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
return result
}
} else if (dict["type"] as! NSString) == "cameraVideo" {
var asFile = false
if let document = dict["document"] as? NSNumber, document.boolValue {
asFile = true
}
let url: String? = (dict["url"] as? String) ?? (dict["url"] as? URL)?.absoluteString
if let url = url, let previewImage = dict["previewImage"] as? UIImage {
let dimensions = previewImage.pixelSize()
let duration = (dict["duration"]! as AnyObject).doubleValue!
var result: [AnyHashable: Any] = [:]
result["item" as NSString] = LegacyAssetItemWrapper(item: .video(data: .tempFile(path: url, dimensions: dimensions, duration: duration), previewImage: dict["previewImage"] as? UIImage, adjustments: dict["adjustments"] as? TGVideoEditAdjustments, caption: caption, asFile: asFile, asAnimation: false), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
return result
}
}
return nil
}
}
func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -> Signal<[EnqueueMessage], NoError> {
return Signal { subscriber in
let disposable = SSignal.combineSignals(signals).start(next: { anyValues in
var messages: [EnqueueMessage] = []
outer: for item in (anyValues as! NSArray) {
if let item = (item as? NSDictionary)?.object(forKey: "item") as? LegacyAssetItemWrapper {
switch item.item {
case let .image(data, caption):
switch data {
case let .image(image):
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let tempFilePath = NSTemporaryDirectory() + "\(randomId).jpeg"
let scaledSize = image.size.aspectFitted(CGSize(width: 1280.0, height: 1280.0))
if let scaledImage = TGScaleImageToPixelSize(image, scaledSize) {
if let scaledImageData = compressImageToJPEG(scaledImage, quality: 0.42) {
let _ = try? scaledImageData.write(to: URL(fileURLWithPath: tempFilePath))
#if DEBUG
if #available(iOSApplicationExtension 11.0, *) {
if false, let heicData = compressImage(scaledImage, quality: 0.65) {
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let _ = try? heicData.write(to: URL(fileURLWithPath: tempFilePath + ".heic"))
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath + ".heic", randomId: randomId)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), reference: nil, resource: resource, previewRepresentations: [], mimeType: "image/heic", size: nil, attributes: [.FileName(fileName: "image.heic")])
var attributes: [MessageAttribute] = []
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
}
messages.append(.message(text: caption ?? "", attributes: attributes, media: media, replyToMessageId: nil, localGroupingKey: item.groupedId))
}
}
#endif
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId)
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: scaledSize, resource: resource)], reference: nil)
var attributes: [MessageAttribute] = []
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
}
messages.append(.message(text: caption ?? "", attributes: attributes, media: media, replyToMessageId: nil, localGroupingKey: item.groupedId))
}
}
case let .asset(asset):
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
let scaledSize = size.aspectFitted(CGSize(width: 1280.0, height: 1280.0))
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier)
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: scaledSize, resource: resource)], reference: nil)
var attributes: [MessageAttribute] = []
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
}
messages.append(.message(text: caption ?? "", attributes: attributes, media: media, replyToMessageId: nil, localGroupingKey: item.groupedId))
case .tempFile:
break
}
case let .file(data, mimeType, name, caption):
switch data {
case let .tempFile(path):
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let resource = LocalFileReferenceMediaResource(localFilePath: path, randomId: randomId)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), reference: nil, resource: resource, previewRepresentations: [], mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)])
messages.append(.message(text: caption ?? "", attributes: [], media: media, replyToMessageId: nil, localGroupingKey: item.groupedId))
case let .asset(asset):
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier)
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), reference: nil, resource: resource, previewRepresentations: [], mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)])
messages.append(.message(text: caption ?? "", attributes: [], media: media, replyToMessageId: nil, localGroupingKey: item.groupedId))
default:
break
}
case let .video(data, previewImage, adjustments, caption, asFile, asAnimation):
var finalDimensions: CGSize
var finalDuration: Double
switch data {
case let .asset(asset):
finalDimensions = asset.dimensions
finalDuration = asset.videoDuration
case let .tempFile(_, dimensions, duration):
finalDimensions = dimensions
finalDuration = duration
}
if !asAnimation {
finalDimensions = TGFitSize(finalDimensions, CGSize(width: 848.0, height: 848.0))
}
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if let previewImage = previewImage {
let resource = LocalFileMediaResource(fileId: arc4random64())
let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 90.0, height: 90.0))
let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)!
if let thumbnailData = UIImageJPEGRepresentation(thumbnailImage, 0.4) {
account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData)
previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: thumbnailSize, resource: resource))
}
}
if !asAnimation {
finalDimensions = TGMediaVideoConverter.dimensions(for: finalDimensions, adjustments: adjustments, preset: TGMediaVideoConversionPresetCompressedMedium)
}
var resourceAdjustments: VideoMediaResourceAdjustments?
if let adjustments = adjustments {
if adjustments.trimApplied() {
finalDuration = adjustments.trimEndValue - adjustments.trimStartValue
}
let adjustmentsData = MemoryBuffer(data: NSKeyedArchiver.archivedData(withRootObject: adjustments.dictionary()))
let digest = MemoryBuffer(data: adjustmentsData.md5Digest())
resourceAdjustments = VideoMediaResourceAdjustments(data: adjustmentsData, digest: digest)
}
let resource: TelegramMediaResource
var fileName: String = "video.mp4"
switch data {
case let .asset(asset):
if let assetFileName = asset.fileName, !assetFileName.isEmpty {
fileName = (assetFileName as NSString).lastPathComponent
}
resource = VideoLibraryMediaResource(localIdentifier: asset.backingAsset.localIdentifier, conversion: asFile ? .passthrough : .compress(resourceAdjustments))
case let .tempFile(path, _, _):
if asFile || asAnimation {
if let size = fileSize(path) {
resource = LocalFileMediaResource(fileId: arc4random64(), size: size)
account.postbox.mediaBox.moveResourceData(resource.id, fromTempPath: path)
} else {
continue outer
}
} else {
resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: path, adjustments: resourceAdjustments)
}
}
var fileAttributes: [TelegramMediaFileAttribute] = []
fileAttributes.append(.FileName(fileName: fileName))
if asAnimation {
fileAttributes.append(.Animated)
}
if !asFile {
fileAttributes.append(.Video(duration: Int(finalDuration), size: finalDimensions, flags: []))
if let adjustments = adjustments {
if adjustments.sendAsGif {
fileAttributes.append(.Animated)
}
}
}
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), reference: nil, resource: resource, previewRepresentations: previewRepresentations, mimeType: "video/mp4", size: nil, attributes: fileAttributes)
var attributes: [MessageAttribute] = []
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
}
messages.append(.message(text: caption ?? "", attributes: attributes, media: media, replyToMessageId: nil, localGroupingKey: item.groupedId))
}
}
}
subscriber.putNext(messages)
subscriber.putCompletion()
}, error: { _ in
subscriber.putError(NoError())
}, completed: nil)
return ActionDisposable {
disposable?.dispose()
}
}
}
func legacyAssetPickerDataSignals(account: Account, signals: [Any]) -> Signal<[TelegramMediaResource], NoError> {
return Signal { subscriber in
let disposable = SSignal.combineSignals(signals).start(next: { anyValues in
var datas: [TelegramMediaResource] = []
outer: for item in (anyValues as! NSArray) {
if let item = (item as? NSDictionary)?.object(forKey: "item") as? LegacyAssetItemWrapper {
switch item.item {
case let .image(data, _):
switch data {
case let .image(image):
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let tempFilePath = NSTemporaryDirectory() + "\(randomId).jpeg"
let scaledSize = image.size.aspectFitted(CGSize(width: 2048.0, height: 2048.0))
if let scaledImage = TGScaleImageToPixelSize(image, scaledSize) {
if let scaledImageData = compressImageToJPEG(scaledImage, quality: 0.84) {
let _ = try? scaledImageData.write(to: URL(fileURLWithPath: tempFilePath))
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId)
datas.append(resource)
}
}
case let .asset(asset):
break
/*var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
let scaledSize = size.aspectFitted(CGSize(width: 1280.0, height: 1280.0))
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier)
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: scaledSize, resource: resource)], reference: nil)
var attributes: [MessageAttribute] = []
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
}
messages.append(.message(text: caption ?? "", attributes: attributes, media: media, replyToMessageId: nil, localGroupingKey: item.groupedId))*/
case .tempFile:
break
}
case let .file(data, mimeType, name, caption):
switch data {
case let .tempFile(path):
var randomId: Int64 = 0
arc4random_buf(&randomId, 8)
let resource = LocalFileReferenceMediaResource(localFilePath: path, randomId: randomId)
datas.append(resource)
default:
break
}
case .video:
break
}
}
}
subscriber.putNext(datas)
subscriber.putCompletion()
}, error: { _ in
subscriber.putError(NoError())
}, completed: nil)
return ActionDisposable {
disposable?.dispose()
}
}
}