Swiftgram/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift
2020-07-01 04:43:39 +03:00

160 lines
7.0 KiB
Swift

import Foundation
import UIKit
import SwiftSignalKit
import Postbox
import TelegramCore
import SyncCore
import Photos
import Display
import MobileCoreServices
import DeviceAccess
import AccountContext
import LegacyComponents
public enum FetchMediaDataState {
case progress(Float)
case data(MediaResourceData)
}
public func fetchMediaData(context: AccountContext, postbox: Postbox, mediaReference: AnyMediaReference) -> Signal<(FetchMediaDataState, Bool), NoError> {
var resource: MediaResource?
var isImage = true
var fileExtension: String?
if let image = mediaReference.media as? TelegramMediaImage {
if let representation = largestImageRepresentation(image.representations) {
resource = representation.resource
}
} else if let file = mediaReference.media as? TelegramMediaFile {
resource = file.resource
if file.isVideo || file.mimeType.hasPrefix("video/") {
isImage = false
}
let maybeExtension = ((file.fileName ?? "") as NSString).pathExtension
if !maybeExtension.isEmpty {
fileExtension = maybeExtension
}
} else if let webpage = mediaReference.media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
if let file = content.file {
resource = file.resource
if file.isVideo {
isImage = false
}
} else if let image = content.image {
if let representation = largestImageRepresentation(image.representations) {
resource = representation.resource
}
}
}
if let resource = resource {
let fetchedData: Signal<FetchMediaDataState, NoError> = Signal { subscriber in
let fetched = fetchedMediaResource(mediaBox: postbox.mediaBox, reference: mediaReference.resourceReference(resource)).start()
let status = postbox.mediaBox.resourceStatus(resource).start(next: { status in
switch status {
case .Local:
subscriber.putNext(.progress(1.0))
case .Remote:
subscriber.putNext(.progress(0.0))
case let .Fetching(_, progress):
subscriber.putNext(.progress(progress))
}
})
let data = postbox.mediaBox.resourceData(resource, pathExtension: fileExtension, option: .complete(waitUntilFetchStatus: true)).start(next: { next in
subscriber.putNext(.data(next))
}, completed: {
subscriber.putCompletion()
})
return ActionDisposable {
fetched.dispose()
status.dispose()
data.dispose()
}
}
return fetchedData
|> map { data in
return (data, isImage)
}
} else {
return .complete()
}
}
public func saveToCameraRoll(context: AccountContext, postbox: Postbox, mediaReference: AnyMediaReference) -> Signal<Float, NoError> {
return fetchMediaData(context: context, postbox: postbox, mediaReference: mediaReference)
|> mapToSignal { state, isImage -> Signal<Float, NoError> in
switch state {
case let .progress(value):
return .single(value)
case let .data(data):
if data.complete {
return Signal<Float, NoError> { subscriber in
DeviceAccess.authorizeAccess(to: .mediaLibrary(.save), presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: { c, a in
context.sharedContext.presentGlobalController(c, a)
}, openSettings: context.sharedContext.applicationBindings.openSettings, { authorized in
if !authorized {
subscriber.putCompletion()
return
}
let tempVideoPath = NSTemporaryDirectory() + "\(arc4random64()).mp4"
PHPhotoLibrary.shared().performChanges({
if isImage {
if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
PHAssetCreationRequest.forAsset().addResource(with: .photo, data: fileData, options: nil)
} else {
if let image = UIImage(data: fileData) {
PHAssetChangeRequest.creationRequestForAsset(from: image)
}
}
}
} else {
if let _ = try? FileManager.default.copyItem(atPath: data.path, toPath: tempVideoPath) {
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: tempVideoPath))
}
}
}, completionHandler: { _, error in
if let error = error {
print("\(error)")
}
let _ = try? FileManager.default.removeItem(atPath: tempVideoPath)
subscriber.putNext(1.0)
subscriber.putCompletion()
})
})
return ActionDisposable {
}
}
} else {
return .complete()
}
}
}
}
public func copyToPasteboard(context: AccountContext, postbox: Postbox, mediaReference: AnyMediaReference) -> Signal<Void, NoError> {
return fetchMediaData(context: context, postbox: postbox, mediaReference: mediaReference)
|> mapToSignal { state, isImage -> Signal<Void, NoError> in
if case let .data(data) = state, data.complete {
return Signal<Void, NoError> { subscriber in
let pasteboard = UIPasteboard.general
pasteboard.setPersistent(true)
if mediaReference.media is TelegramMediaImage {
if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: .mappedIfSafe) {
pasteboard.setData(fileData, forPasteboardType: kUTTypeJPEG as String)
}
}
subscriber.putNext(Void())
subscriber.putCompletion()
return EmptyDisposable
}
} else {
return .complete()
}
}
|> mapToSignal { _ -> Signal<Void, NoError> in return .complete() }
}