Swiftgram/submodules/LegacyUI/Sources/LegacyLocationVenueIconDataSource.swift
2019-10-21 16:58:00 +04:00

194 lines
7.8 KiB
Swift

import Foundation
import UIKit
import LegacyComponents
import Postbox
import TelegramCore
import SyncCore
import SwiftSignalKit
import Display
private let sharedImageCache = TGMemoryImageCache(softMemoryLimit: 2 * 1024 * 1024, hardMemoryLimit: 3 * 1024 * 1024)!
private let placeholderImage = generateFilledCircleImage(diameter: 40.0, color: UIColor(rgb: 0xf2f2f2))
private final class LegacyLocationVenueIconTask: NSObject {
private let disposable = DisposableSet()
init(account: Account, url: String, completion: @escaping (Data?) -> Void) {
super.init()
let resource = HttpReferenceMediaResource(url: url, size: nil)
self.disposable.add(account.postbox.mediaBox.resourceData(resource).start(next: { data in
if data.complete {
if let loadedData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
completion(loadedData)
}
}
}))
self.disposable.add(account.postbox.mediaBox.fetchedResource(resource, parameters: nil).start())
}
deinit {
self.disposable.dispose()
}
func cancel() {
self.disposable.dispose()
}
}
private let genericIconImage = TGComponentsImageNamed("LocationMessagePinIcon")?.precomposed()
final class LegacyLocationVenueIconDataSource: TGImageDataSource {
private let account: () -> Account?
init(account: @escaping () -> Account?) {
self.account = account
super.init()
}
override func canHandleUri(_ uri: String!) -> Bool {
if let uri = uri {
if uri.hasPrefix("location-venue-icon://") {
return true
}
}
return false
}
override func loadAttributeSync(forUri uri: String!, attribute: String!) -> Any! {
if attribute == "placeholder" {
return placeholderImage
}
return nil
}
override func loadDataSync(withUri uri: String!, canWait: Bool, acceptPartialData: Bool, asyncTaskId: AutoreleasingUnsafeMutablePointer<AnyObject?>!, progress: ((Float) -> Void)!, partialCompletion: ((TGDataResource?) -> Void)!, completion: ((TGDataResource?) -> Void)!) -> TGDataResource! {
if let image = sharedImageCache.image(forKey: uri, attributes: nil) {
return TGDataResource(image: image, decoded: true)
}
return nil
}
private static func unavailableImage(for uri: String) -> TGDataResource? {
let args: [AnyHashable : Any]
let argumentsString = String(uri[uri.index(uri.startIndex, offsetBy: "location-venue-icon://".count)...])
args = TGStringUtils.argumentDictionary(inUrlString: argumentsString)!
guard let width = Int((args["width"] as! String)), width > 1 else {
return nil
}
guard let height = Int((args["height"] as! String)), height > 1 else {
return nil
}
guard let colorN = (args["color"] as? String).flatMap({ Int($0) }) else {
return nil
}
let color = UIColor(rgb: UInt32(colorN))
let size = CGSize(width: CGFloat(width), height: CGFloat(height))
guard let iconSourceImage = genericIconImage.flatMap({ generateTintedImage(image: $0, color: color) }) else {
return nil
}
UIGraphicsBeginImageContextWithOptions(iconSourceImage.size, false, iconSourceImage.scale)
var context = UIGraphicsGetCurrentContext()!
iconSourceImage.draw(at: CGPoint())
context.setBlendMode(.sourceAtop)
context.setFillColor(color.cgColor)
context.fill(CGRect(origin: CGPoint(), size: iconSourceImage.size))
let tintedIconImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
context = UIGraphicsGetCurrentContext()!
let fitSize = CGSize(width: size.width - 4.0 * 2.0, height: size.height - 4.0 * 2.0)
let imageSize = iconSourceImage.size.aspectFitted(fitSize)
let imageRect = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)
tintedIconImage?.draw(in: imageRect)
let iconImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
if let iconImage = iconImage {
sharedImageCache.setImage(iconImage, forKey: uri, attributes: nil)
return TGDataResource(image: iconImage, decoded: true)
}
return nil
}
override func loadDataAsync(withUri uri: String!, progress: ((Float) -> Void)!, partialCompletion: ((TGDataResource?) -> Void)!, completion: ((TGDataResource?) -> Void)!) -> Any! {
if let account = self.account() {
let args: [AnyHashable : Any]
let argumentsString = String(uri[uri.index(uri.startIndex, offsetBy: "location-venue-icon://".count)...])
args = TGStringUtils.argumentDictionary(inUrlString: argumentsString)!
guard let width = Int((args["width"] as! String)), width > 1 else {
return nil
}
guard let height = Int((args["height"] as! String)), height > 1 else {
return nil
}
guard let colorN = (args["color"] as? String).flatMap({ Int($0) }) else {
return nil
}
guard let type = args["type"] as? String else {
return LegacyLocationVenueIconDataSource.unavailableImage(for: uri)
}
let color = UIColor(rgb: UInt32(colorN))
let url = "https://ss3.4sqi.net/img/categories_v2/\(type)_88.png"
let size = CGSize(width: CGFloat(width), height: CGFloat(height))
return LegacyLocationVenueIconTask(account: account, url: url, completion: { data in
if let data = data, let iconSourceImage = UIImage(data: data) {
UIGraphicsBeginImageContextWithOptions(iconSourceImage.size, false, iconSourceImage.scale)
var context = UIGraphicsGetCurrentContext()!
iconSourceImage.draw(at: CGPoint())
context.setBlendMode(.sourceAtop)
context.setFillColor(color.cgColor)
context.fill(CGRect(origin: CGPoint(), size: iconSourceImage.size))
let tintedIconImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
context = UIGraphicsGetCurrentContext()!
let imageRect = CGRect(x: 4.0, y: 4.0, width: size.width - 4.0 * 2.0, height: size.height - 4.0 * 2.0)
tintedIconImage?.draw(in: imageRect)
let iconImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext()
if let iconImage = iconImage {
sharedImageCache.setImage(iconImage, forKey: uri, attributes: nil)
completion?(TGDataResource(image: iconImage, decoded: true))
}
} else {
if let image = LegacyLocationVenueIconDataSource.unavailableImage(for: uri) {
completion?(image)
}
}
})
} else {
return nil
}
}
override func cancelTask(byId taskId: Any!) {
if let disposable = taskId as? LegacyLocationVenueIconTask {
disposable.cancel()
}
}
}