diff --git a/ChattoAdditions/Source/Chat Items/PhotoMessages/PhotoMessageModel.swift b/ChattoAdditions/Source/Chat Items/PhotoMessages/PhotoMessageModel.swift index 5d79e19e5..df4cf7812 100644 --- a/ChattoAdditions/Source/Chat Items/PhotoMessages/PhotoMessageModel.swift +++ b/ChattoAdditions/Source/Chat Items/PhotoMessages/PhotoMessageModel.swift @@ -6,7 +6,11 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + to use, copy + public var thumbnail: UIImage + + public var thumbnailURL: String? + , modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -28,6 +32,8 @@ public protocol PhotoMessageModelProtocol: DecoratedMessageModelProtocol { var image: UIImage { get } var imageSize: CGSize { get } var imageURL: String? { get } + var thumbnail: UIImage { get } + var thumbnailURL: String? { get } } open class PhotoMessageModel: PhotoMessageModelProtocol { @@ -38,10 +44,15 @@ open class PhotoMessageModel: PhotoMessageM public var image: UIImage public let imageSize: CGSize public var imageURL: String? - public init(messageModel: MessageModelT, imageSize: CGSize, image: UIImage, imageURL: String?) { + public var thumbnail: UIImage + public var thumbnailURL: String? + + public init(messageModel: MessageModelT, imageSize: CGSize, image: UIImage, imageURL: String?, thumbnailURL: String?) { self._messageModel = messageModel self.imageSize = imageSize self.image = image self.imageURL = imageURL + self.thumbnail = image + self.thumbnailURL = thumbnailURL } } diff --git a/ChattoAdditions/Source/Chat Items/PhotoTextMessages/PhotoTextMessageModel.swift b/ChattoAdditions/Source/Chat Items/PhotoTextMessages/PhotoTextMessageModel.swift index 3531f8bf8..239eabd69 100644 --- a/ChattoAdditions/Source/Chat Items/PhotoTextMessages/PhotoTextMessageModel.swift +++ b/ChattoAdditions/Source/Chat Items/PhotoTextMessages/PhotoTextMessageModel.swift @@ -19,11 +19,17 @@ open class PhotoTextMessageModel: PhotoText public var image: UIImage public let imageSize: CGSize public var imageURL: String? - public init(messageModel: MessageModelT, text: String, imageSize: CGSize, image: UIImage, imageURL: String?) { + public var thumbnail: UIImage + public var thumbnailURL: String? + + + public init(messageModel: MessageModelT, text: String, imageSize: CGSize, image: UIImage, imageURL: String?, thumbnailURL: String?) { self._messageModel = messageModel self.text = text self.imageSize = imageSize self.image = image self.imageURL = imageURL + self.thumbnail = image + self.thumbnailURL = thumbnailURL } } diff --git a/ChattoAdditions/Source/Chat Items/TextMessages/Views/TextBubbleView.swift b/ChattoAdditions/Source/Chat Items/TextMessages/Views/TextBubbleView.swift index a800a000e..4ef1785af 100644 --- a/ChattoAdditions/Source/Chat Items/TextMessages/Views/TextBubbleView.swift +++ b/ChattoAdditions/Source/Chat Items/TextMessages/Views/TextBubbleView.swift @@ -326,9 +326,15 @@ private final class ChatMessageTextView: UITextView { super.gestureRecognizers = newValue } get { - return super.gestureRecognizers?.filter({ (gestureRecognizer) -> Bool in - return type(of: gestureRecognizer) == UILongPressGestureRecognizer.self && gestureRecognizer.delaysTouchesEnded - }) + return super.gestureRecognizers?.filter { gestureRecognizer in + if type(of: gestureRecognizer) == UILongPressGestureRecognizer.self, gestureRecognizer.delaysTouchesEnded { + return true + } + if #available(iOS 11, *), gestureRecognizer.name == "UITextInteractionNameLinkTap" { + return true + } + return false + } } } diff --git a/ChattoAdditions/Source/Chat Items/VideoTextMessages/VideoTextMessageModel.swift b/ChattoAdditions/Source/Chat Items/VideoTextMessages/VideoTextMessageModel.swift index 906a9ff85..48d178591 100644 --- a/ChattoAdditions/Source/Chat Items/VideoTextMessages/VideoTextMessageModel.swift +++ b/ChattoAdditions/Source/Chat Items/VideoTextMessages/VideoTextMessageModel.swift @@ -19,20 +19,24 @@ open class VideoTextMessageModel: VideoText public var image: UIImage public let imageSize: CGSize public var imageURL: String? + public var thumbnail: UIImage + public var thumbnailURL: String? public var videoURL: URL? public init(messageModel: MessageModelT, text: String, videoURL: URL, imageSize: CGSize, - image: UIImage, - imageURL: String?) + thumbnail: UIImage, + thumbnailURL: String?) { self._messageModel = messageModel self.text = text self.imageSize = imageSize - self.image = image - self.imageURL = imageURL + self.image = thumbnail + self.thumbnail = thumbnail + self.imageURL = thumbnailURL + self.thumbnailURL = thumbnailURL self.videoURL = videoURL } } diff --git a/ChattoAdditions/Source/Input/Photos/Photo/PhotosInputCell.swift b/ChattoAdditions/Source/Input/Photos/Photo/PhotosInputCell.swift index 425c4999b..ab95e5786 100644 --- a/ChattoAdditions/Source/Input/Photos/Photo/PhotosInputCell.swift +++ b/ChattoAdditions/Source/Input/Photos/Photo/PhotosInputCell.swift @@ -71,6 +71,11 @@ final class PhotosInputCell: UICollectionViewCell { } set { self.imageView.image = newValue + if (newValue != nil) { + self.imageView.startAnimating() + } else { + self.imageView.stopAnimating() + } } } diff --git a/ChattoAdditions/Source/Input/Photos/Photo/PhotosInputDataProvider.swift b/ChattoAdditions/Source/Input/Photos/Photo/PhotosInputDataProvider.swift index 3446b1e6f..e7c185cd2 100644 --- a/ChattoAdditions/Source/Input/Photos/Photo/PhotosInputDataProvider.swift +++ b/ChattoAdditions/Source/Input/Photos/Photo/PhotosInputDataProvider.swift @@ -89,7 +89,7 @@ final class PhotosInputDataProvider: NSObject, PhotosInputDataProviderProtocol, } func requestPreviewImage(at index: Int, - targetSize: CGSize, + targetSize: CGSize, completion: @escaping PhotosInputDataProviderCompletion) -> PhotosInputDataProviderImageRequestProtocol { assert(index >= 0 && index < self.fetchResult.count, "Index out of bounds") let asset = self.fetchResult[index] @@ -134,8 +134,18 @@ final class PhotosInputDataProvider: NSObject, PhotosInputDataProviderProtocol, requestId = self.imageManager.requestImageData(for: asset, options: options, resultHandler: { [weak self] (data, _, _, info) in guard let sSelf = self else { return } let result: PhotosInputDataProviderResult - if let data = data, let image = UIImage(data: data) { - result = .success(image) + if let data = data { + if UIImage.isAnimatedImage(data) { + if let gifImage = UIImage.gifImageWithData(data: data) { + result = .success(gifImage) + } else { + result = .error(info?[PHImageErrorKey] as? Error) + } + } else if let image = UIImage(data: data) { + result = .success(image) + } else { + result = .error(info?[PHImageErrorKey] as? Error) + } } else { result = .error(info?[PHImageErrorKey] as? Error) } @@ -194,3 +204,129 @@ final class PhotosInputDataProvider: NSObject, PhotosInputDataProviderProtocol, } } } + +public extension UIImage { + + class func isAnimatedImage(_ data: Data) -> Bool { + if let source = CGImageSourceCreateWithData(data as CFData, nil) { + let count = CGImageSourceGetCount(source) + return count > 1 + } + return false + } + + class func gifImageWithData(data: Data) -> UIImage? { + guard let source = CGImageSourceCreateWithData(data as CFData, nil) else { + print("image doesn't exist") + return nil + } + + return UIImage.animatedImageWithSource(source: source) + } + + class func animatedImageWithSource(source: CGImageSource) -> UIImage? { + let count = CGImageSourceGetCount(source) + var images = [CGImage]() + var delays = [Int]() + + for i in 0.. Double { + var delay = 0.1 + + let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) + let gifProperties: CFDictionary = unsafeBitCast(CFDictionaryGetValue(cfProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()), to: CFDictionary.self) + + var delayObject: AnyObject = unsafeBitCast(CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()), to: AnyObject.self) + + if delayObject.doubleValue == 0 { + delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self) + } + + delay = delayObject as! Double + return delay + } + + class func gcdForPair(a: Int?, _ b: Int?) -> Int { + var a = a + var b = b + if b == nil || a == nil { + if b != nil { + return b! + } else if a != nil { + return a! + } else { + return 0 + } + } + + if a! < b! { + let c = a! + a = b! + b = c + } + + var rest: Int + while true { + rest = a! % b! + + if rest == 0 { + return b! + } else { + a = b! + b = rest + } + } + } + + class func gcdForArray(array: Array) -> Int { + if array.isEmpty { + return 1 + } + + var gcd = array[0] + + for val in array { + gcd = UIImage.gcdForPair(a: val, gcd) + } + + return gcd + } + +}