ChatAnimatedGifMessageCell.swift 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2019-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import Foundation
  21. import CocoaLumberjackSwift
  22. @objc open class ChatAnimatedGifMessageCell: ChatBlobTextMessageCell {
  23. override open var message: BaseMessage! {
  24. didSet {
  25. setBaseMessage(newMessage: message)
  26. }
  27. }
  28. private var _imageContentView: FLAnimatedImageView?
  29. private var _playButtonView: UIImageView?
  30. private var _animatedImage: FLAnimatedImage?
  31. private var _thumbnailImage: UIImage?
  32. private var _imageIcon: UIImageView?
  33. private var _downloadBackground: UIImageView?
  34. private var _downloadSizeLabel: UILabel?
  35. private var _gifDispatchQueue = DispatchQueue.init(label: "ch.threema.gif")
  36. @objc override public init!(style: UITableViewCell.CellStyle, reuseIdentifier: String!, transparent: Bool) {
  37. super.init(style: style, reuseIdentifier: reuseIdentifier, transparent: transparent)
  38. setBubbleHighlighted(false)
  39. setupViews()
  40. }
  41. required public init?(coder: NSCoder) {
  42. fatalError("init(coder:) has not been implemented")
  43. }
  44. }
  45. extension ChatAnimatedGifMessageCell {
  46. // MARK: Private functions
  47. private func setupViews() {
  48. _imageContentView = FLAnimatedImageView()
  49. _imageContentView?.clearsContextBeforeDrawing = false
  50. contentView.addSubview(_imageContentView!)
  51. if #available(iOS 13.0, *) {
  52. _playButtonView = UIImageView.init(image: BundleUtil.imageNamed("Play")?.withTintColor(Colors.white()))
  53. } else {
  54. _playButtonView = UIImageView.init(image: BundleUtil.imageNamed("Play")?.withTint(Colors.white()))
  55. }
  56. _playButtonView?.frame = CGRect.init(x: 0.0, y: 0.0, width: 32.0, height: 32.0)
  57. _playButtonView?.alpha = 0.8
  58. _imageContentView!.addSubview(_playButtonView!)
  59. _imageIcon = UIImageView.init(image: BundleUtil.imageNamed("Landscape")?.withTint(Colors.fontLight()))
  60. _imageIcon?.clearsContextBeforeDrawing = false
  61. contentView.addSubview(_imageIcon!)
  62. _captionLabel = ChatTextMessageCell.makeAttributedLabel(withFrame: self.bounds)
  63. _captionLabel?.tapDelegate = self
  64. _captionLabel?.longPressDelegate = self
  65. contentView.addSubview(_captionLabel!)
  66. _downloadBackground = UIImageView.init(image: UIImage.init(named: "VideoDownloadBg")?.resizableImage(withCapInsets: UIEdgeInsets.init(top: 0, left: 32, bottom: 0, right: 0)))
  67. _downloadBackground!.isOpaque = false
  68. _downloadSizeLabel = UILabel.init()
  69. _downloadSizeLabel?.backgroundColor = .clear
  70. _downloadSizeLabel?.isOpaque = false
  71. _downloadSizeLabel?.font = UIFont.boldSystemFont(ofSize: 12)
  72. _downloadSizeLabel?.textColor = Colors.white()
  73. _downloadSizeLabel?.textAlignment = .right
  74. _downloadSizeLabel?.adjustsFontSizeToFitWidth = true
  75. _downloadBackground!.addSubview(_downloadSizeLabel!)
  76. _imageContentView!.addSubview(_downloadSizeLabel!)
  77. if #available(iOS 11.0, *) {
  78. _imageContentView?.accessibilityIgnoresInvertColors = true
  79. }
  80. setupColors()
  81. }
  82. private func setupAnimatedImage() {
  83. let fileMessage = message as! FileMessage
  84. _gifDispatchQueue.async {
  85. self._animatedImage = FLAnimatedImage.init(animatedGIFData: fileMessage.data.data)
  86. self.enableAnimation(true)
  87. }
  88. }
  89. private func enableAnimation(_ enable: Bool) {
  90. DispatchQueue.main.async {
  91. if (self._animatedImage != nil) && enable {
  92. self._imageContentView?.animatedImage = self._animatedImage
  93. self._imageContentView?.isHidden = false
  94. self._imageIcon?.isHidden = true
  95. self._playButtonView?.isHidden = true
  96. self._imageContentView?.startAnimating()
  97. }
  98. else if (self._thumbnailImage != nil) {
  99. self._imageContentView?.image = self._thumbnailImage
  100. self._imageContentView?.isHidden = false
  101. self._imageIcon?.isHidden = true
  102. self._playButtonView?.isHidden = false
  103. }
  104. else {
  105. self._imageIcon?.isHidden = false
  106. self._imageContentView?.isHidden = true
  107. self._playButtonView?.isHidden = true
  108. }
  109. }
  110. }
  111. private func toggleAnimation() {
  112. enableAnimation(!_imageContentView!.isAnimating)
  113. }
  114. }
  115. extension ChatAnimatedGifMessageCell {
  116. // MARK: Override functions
  117. @objc override open class func height(for message: BaseMessage!, forTableWidth tableWidth: CGFloat) -> CGFloat {
  118. let fileMessage = message as! FileMessage
  119. var cellHeight: CGFloat = 40.0
  120. let imageInsets = UIEdgeInsets.init(top: 5, left: 5, bottom: 5, right: 5)
  121. var scaledSize = CGSize.init()
  122. if fileMessage.thumbnail == nil || fileMessage.thumbnail.height.floatValue <= 0.0 {
  123. // workaround for backward compatibility, create new thumbnail when not available yet or invalid
  124. let animImage = FLAnimatedImage.init(animatedGIFData: fileMessage.data.data)
  125. if let thumbnail = MediaConverter.getThumbnailFor(animImage?.posterImage) {
  126. let entityManager = EntityManager.init()
  127. entityManager.performAsyncBlockAndSafe {
  128. let dbThumbnail = entityManager.entityCreator.imageData()
  129. dbThumbnail?.data = thumbnail.jpegData(compressionQuality: CGFloat(kJPEGCompressionQuality))
  130. dbThumbnail?.width = NSNumber(value: Float(thumbnail.size.width))
  131. dbThumbnail?.height = NSNumber(value: Float(thumbnail.size.height))
  132. fileMessage.thumbnail = dbThumbnail
  133. }
  134. }
  135. }
  136. if fileMessage.thumbnail != nil {
  137. if fileMessage.thumbnail.data != nil && fileMessage.thumbnail.height.floatValue > 0 {
  138. let width: CGFloat = CGFloat(fileMessage.thumbnail.width.floatValue)
  139. let height: CGFloat = CGFloat(fileMessage.thumbnail.height.floatValue)
  140. let size: CGSize = CGSize.init(width: width, height: height)
  141. scaledSize = ChatAnimatedGifMessageCell.scaleImageSize(toCell: size, forTableWidth: tableWidth)
  142. if scaledSize.height != scaledSize.height || scaledSize.height < 0 {
  143. scaledSize.height = 40.0
  144. }
  145. cellHeight = scaledSize.height - 17.0
  146. }
  147. }
  148. if let caption = fileMessage.getCaption(), caption.count > 0 {
  149. let x: CGFloat = 30.0
  150. let maxSize = CGSize.init(width: scaledSize.width - x, height: CGFloat.greatestFiniteMagnitude)
  151. var textSize: CGSize?
  152. let captionTextNSString = NSString.init(string: caption)
  153. if UserSettings.shared().disableBigEmojis && captionTextNSString.isOnlyEmojisMaxCount(3) {
  154. var dummyLabelEmoji: ZSWTappableLabel? = nil
  155. if dummyLabelEmoji == nil {
  156. dummyLabelEmoji = ChatTextMessageCell.makeAttributedLabel(withFrame: CGRect.init(x: (x/2), y: 0.0, width: maxSize.width, height: maxSize.height))
  157. }
  158. dummyLabelEmoji!.font = ChatTextMessageCell.emojiFont()
  159. dummyLabelEmoji?.attributedText = NSAttributedString.init(string: caption, attributes: [NSAttributedString.Key.font: ChatMessageCell.emojiFont()!])
  160. textSize = dummyLabelEmoji?.sizeThatFits(maxSize)
  161. textSize!.height = textSize!.height + 23.0
  162. } else {
  163. var dummyLabel: ZSWTappableLabel? = nil
  164. if dummyLabel == nil {
  165. dummyLabel = ChatTextMessageCell.makeAttributedLabel(withFrame: CGRect.init(x: (x/2), y: 0.0, width: maxSize.width, height: maxSize.height))
  166. }
  167. dummyLabel!.font = ChatTextMessageCell.textFont()
  168. let attributed = TextStyleUtils.makeAttributedString(from: caption, with: dummyLabel!.font, textColor: Colors.fontNormal(), isOwn: true, application: UIApplication.shared)
  169. let formattedAttributeString = NSMutableAttributedString.init(attributedString: (dummyLabel!.applyMarkup(for: attributed))!)
  170. dummyLabel?.attributedText = TextStyleUtils.makeMentionsAttributedString(for: formattedAttributeString, textFont: dummyLabel!.font!, at: dummyLabel!.textColor.withAlphaComponent(0.4), messageInfo: Int32(message.isOwn!.intValue), application: UIApplication.shared)
  171. textSize = dummyLabel?.sizeThatFits(maxSize)
  172. textSize!.height = textSize!.height + 23.0
  173. }
  174. cellHeight = cellHeight + textSize!.height
  175. } else {
  176. cellHeight += imageInsets.top + imageInsets.bottom
  177. }
  178. return cellHeight
  179. }
  180. override public func layoutSubviews() {
  181. let fileMessage = message as! FileMessage
  182. let imageInsets = UIEdgeInsets.init(top: 5, left: 5, bottom: 5, right: 5)
  183. if (fileMessage.thumbnail != nil) {
  184. var size = CGSize.init(width: CGFloat(fileMessage.thumbnail.width.floatValue), height: CGFloat(fileMessage.thumbnail.height.floatValue))
  185. var textSize: CGSize = CGSize.init(width: 0.0, height: 0.0)
  186. let x: CGFloat = 30.0
  187. /* scale to fit maximum cell size */
  188. size = ChatAnimatedGifMessageCell.scaleImageSize(toCell: size, forTableWidth: frame.size.width)
  189. if size.height != size.height {
  190. size.height = 120.0
  191. }
  192. if size.width != size.width {
  193. size.width = 120.0
  194. }
  195. if let caption = fileMessage.getCaption(), caption.count > 0 {
  196. textSize = _captionLabel!.sizeThatFits(CGSize.init(width: size.width - x, height: CGFloat.greatestFiniteMagnitude))
  197. textSize.height = textSize.height + 12.0
  198. }
  199. let bubbleSize = CGSize.init(width: size.width + imageInsets.left + imageInsets.right, height: size.height + imageInsets.top + imageInsets.bottom + textSize.height)
  200. setBubble(bubbleSize)
  201. super.layoutSubviews()
  202. _imageContentView!.frame = CGRect.init(x: msgBackground.frame.origin.x + imageInsets.left, y: msgBackground.frame.origin.y + imageInsets.top, width: size.width, height: size.height)
  203. _captionLabel!.frame = CGRect.init(x:ceil(msgBackground.frame.origin.x + (x/2)), y: ceil(_imageContentView!.frame.origin.y + _imageContentView!.frame.size.height), width: ceil(textSize.width), height: ceil(textSize.height))
  204. let mask: CALayer = bubbleMaskWithoutArrow(forImageSize: CGSize.init(width: _imageContentView!.frame.size.width, height: _imageContentView!.frame.size.height))
  205. _imageContentView?.layer.mask = mask
  206. _imageContentView?.layer.masksToBounds = true
  207. if fileMessage.isOwn != nil, fileMessage.isOwn.boolValue {
  208. resendButton.frame = CGRect.init(x: _imageContentView!.frame.origin.x - kMessageScreenMargin, y: _imageContentView!.frame.origin.y + (_imageContentView!.frame.size.height - 32) / 2, width: 114, height: 32)
  209. }
  210. progressBar.frame = CGRect.init(x: _imageContentView!.frame.origin.x + 16.0, y: _imageContentView!.frame.origin.y + _imageContentView!.frame.size.height - 40.0, width: size.width - 32.0, height: 16.0)
  211. /* download size label */
  212. _downloadBackground!.frame = CGRect.init(x: 0, y: 1, width: _imageContentView!.frame.size.width + 1, height: 18.0)
  213. _downloadSizeLabel!.frame = CGRect.init(x: _downloadBackground!.frame.size.width / 2, y: 1, width: _downloadBackground!.frame.size.width / 2 - 12, height: 16.0)
  214. if bubbleSize.height > 44.0 && bubbleSize.width > 44.0 {
  215. _playButtonView!.frame = CGRect.init(x: (_imageContentView!.frame.size.width / 2) - 22.0, y: (_imageContentView!.frame.size.height / 2) - 22.0 - 2.0, width: 44.0, height: 44.0)
  216. } else {
  217. var min = Swift.min(bubbleSize.width, bubbleSize.height)
  218. min = min - 20.0
  219. _playButtonView!.frame = CGRect.init(x: (bubbleSize.width / 2) - (min/2), y: (bubbleSize.height / 2) - (min/2) - 2.0, width: min, height: min)
  220. }
  221. }
  222. }
  223. override open func accessibilityLabelForContent() -> String! {
  224. if _captionLabel?.text != nil {
  225. return "\(BundleUtil.localizedString(forKey: "file") ?? "File"). \(_captionLabel!.text!))"
  226. } else {
  227. return BundleUtil.localizedString(forKey: "file")
  228. }
  229. }
  230. override open func showActivityIndicator() -> Bool {
  231. return showProgressBar() == false
  232. }
  233. override open func showProgressBar() -> Bool {
  234. return true
  235. }
  236. override open func messageTapped(_ sender: Any!) {
  237. let fileMessage = message as! FileMessage
  238. if fileMessage.data == nil {
  239. let loader = AnimGifMessageLoader()
  240. loader.start(with: fileMessage, onCompletion: { (baseMessage) in
  241. DDLogInfo("File gif message blob load completed")
  242. self._downloadBackground?.isHidden = true
  243. self.message = baseMessage
  244. }) { (error) in
  245. DDLogInfo("File gif message blob load failed with error: \(error!)")
  246. if (error! as NSError).code != kErrorCodeUserCancelled {
  247. UIAlertTemplate.showAlert(owner: AppDelegate.shared().currentTopViewController(), title: error?.localizedDescription, message: (error! as NSError).localizedFailureReason, actionOk: nil)
  248. }
  249. }
  250. }
  251. toggleAnimation()
  252. }
  253. override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
  254. let fileMessage = message as! FileMessage
  255. let mdmSetup = MDMSetup.init(setup: false)
  256. if action == #selector(resendMessage(_:)) && fileMessage.isOwn.boolValue && fileMessage.sendFailed.boolValue {
  257. return true
  258. }
  259. else if action == #selector(deleteMessage(_:)) && fileMessage.isOwn.boolValue && fileMessage.progress != nil {
  260. /* don't allow messages in progress to be deleted */
  261. return false
  262. }
  263. else if action == #selector(shareMessage(_:)) {
  264. if #available(iOS 13.0, *) {
  265. let mdmSetup = MDMSetup.init(setup: false)
  266. if mdmSetup?.disableShareMedia() == true {
  267. return false;
  268. }
  269. }
  270. return fileMessage.data != nil
  271. }
  272. else if action == #selector(forwardMessage(_:)) {
  273. if #available(iOS 13.0, *) {
  274. return fileMessage.data != nil
  275. } else {
  276. return false;
  277. }
  278. }
  279. else if action == #selector(copyMessage(_:)) && mdmSetup?.disableShareMedia() == true {
  280. return false
  281. }
  282. else if action == #selector(speakMessage(_:)) && _captionLabel?.text != nil {
  283. return true
  284. }
  285. else {
  286. return super.canPerformAction(action, withSender: sender)
  287. }
  288. }
  289. @objc override open func copyMessage(_ menuController: UIMenuController!) {
  290. let fileMessage = message as! FileMessage
  291. if let caption = fileMessage.getCaption(), caption.count > 0 {
  292. UIPasteboard.general.string = fileMessage.getCaption()
  293. } else {
  294. if fileMessage.data != nil, fileMessage.data.data != nil {
  295. UIPasteboard.general.setData(fileMessage.data.data, forPasteboardType: "com.compuserve.gif")
  296. } else {
  297. if fileMessage.thumbnail != nil, fileMessage.thumbnail.data != nil {
  298. UIPasteboard.general.image = UIImage.init(data: fileMessage.thumbnail.data)
  299. }
  300. }
  301. }
  302. }
  303. open override func textForQuote() -> String! {
  304. return (_captionLabel?.text as? String ?? "")
  305. }
  306. open override func performPlayActionForAccessibility() -> Bool {
  307. messageTapped(self)
  308. return true
  309. }
  310. open override func shouldHideBubbleBackground() -> Bool {
  311. let fileMessage = message as? FileMessage
  312. if fileMessage?.thumbnail != nil {
  313. if fileMessage!.thumbnail!.data != nil {
  314. if fileMessage?.type.intValue == 2 {
  315. if let captionText = fileMessage!.getCaption(), captionText.count > 0 {
  316. return false
  317. }
  318. return true
  319. }
  320. }
  321. }
  322. return false
  323. }
  324. open override func previewViewController() -> UIViewController! {
  325. return chatVc.headerView.getPhotoBrowser(at: message, forPeeking: true)
  326. }
  327. open override func previewViewController(for previewingContext: UIViewControllerPreviewing!, viewControllerForLocation location: CGPoint) -> UIViewController! {
  328. if let controller = super.previewViewController(for: previewingContext, viewControllerForLocation: location) {
  329. return controller
  330. }
  331. return chatVc.headerView.getPhotoBrowser(at: message, forPeeking: true)
  332. }
  333. open override func setupColors() {
  334. super.setupColors()
  335. _captionLabel?.textColor = Colors.fontNormal()
  336. }
  337. @available(iOS 13.0, *)
  338. open override func getContextMenu(_ indexPath: IndexPath!, point: CGPoint) -> UIContextMenuConfiguration! {
  339. if self.isEditing == true {
  340. return nil
  341. }
  342. // returns nil if there is no link tapped
  343. if let menu = contextMenuForLink(indexPath, point: point) {
  344. return menu
  345. }
  346. let fileMessage = message as! FileMessage
  347. if fileMessage.data != nil {
  348. if fileMessage.data.data != nil {
  349. let conf = UIContextMenuConfiguration.init(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in
  350. return self.previewViewController()
  351. }) { (suggestedActions) -> UIMenu? in
  352. var menuItems = super.contextMenuItems()!
  353. let saveImage = UIImage.init(systemName: "square.and.arrow.down.fill", compatibleWith: self.traitCollection)
  354. let saveAction = UIAction.init(title: BundleUtil.localizedString(forKey: "save"), image: saveImage, identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { (action) in
  355. let filename = FileUtility.getTemporarySendableFileName(base: "gif")
  356. let tmpFileUrl = fileMessage.tmpURL(filename)
  357. fileMessage.exportData(to: tmpFileUrl)
  358. AlbumManager.shared.save(url: tmpFileUrl!, isVideo: false) { (success) in
  359. do {
  360. try FileManager.default.removeItem(at: tmpFileUrl!)
  361. }
  362. catch {
  363. }
  364. }
  365. }
  366. if self.message.isOwn.boolValue == true || self.chatVc.conversation.isGroup() == true {
  367. menuItems.insert(saveAction, at: 0)
  368. } else {
  369. menuItems.insert(saveAction, at: 1)
  370. }
  371. return UIMenu.init(title: "", image: nil, identifier: nil, options: .displayInline, children: menuItems as! [UIMenuElement])
  372. }
  373. return conf
  374. } else {
  375. return super.getContextMenu(indexPath, point: point)
  376. }
  377. } else {
  378. return super.getContextMenu(indexPath, point: point)
  379. }
  380. }
  381. open override func willDisplay() {
  382. super.willDisplay()
  383. enableAnimation(true)
  384. }
  385. open override func didEndDisplaying() {
  386. super.didEndDisplaying()
  387. enableAnimation(false)
  388. }
  389. }
  390. extension ChatAnimatedGifMessageCell {
  391. // MARK: Public functions
  392. func setBaseMessage(newMessage: BaseMessage) {
  393. _thumbnailImage = nil
  394. _animatedImage = nil
  395. let fileMessage = newMessage as! FileMessage
  396. super.message = newMessage
  397. if fileMessage.thumbnail != nil, let thumb = fileMessage.thumbnail.uiImage {
  398. _thumbnailImage = thumb
  399. }
  400. var size = CGSize.init(width: 80.0, height: 40.0)
  401. if fileMessage.thumbnail != nil {
  402. if fileMessage.thumbnail.data != nil && fileMessage.thumbnail.height.floatValue > 0 {
  403. let width: CGFloat = CGFloat(fileMessage.thumbnail.width.floatValue)
  404. let height: CGFloat = CGFloat(fileMessage.thumbnail.height.floatValue)
  405. let fileMessageSize: CGSize = CGSize.init(width: width, height: height)
  406. size = ChatAnimatedGifMessageCell.scaleImageSize(toCell: fileMessageSize, forTableWidth: frame.size.width)
  407. }
  408. _imageContentView!.frame.size = size
  409. }
  410. var autoresizingMask: AutoresizingMask = .flexibleRightMargin
  411. if fileMessage.isOwn.boolValue {
  412. autoresizingMask = .flexibleLeftMargin
  413. }
  414. _imageContentView?.autoresizingMask = autoresizingMask
  415. _downloadBackground?.autoresizingMask = autoresizingMask
  416. _downloadSizeLabel?.autoresizingMask = autoresizingMask
  417. if let captionText = fileMessage.getCaption(), captionText.count > 0 {
  418. let attributed = TextStyleUtils.makeAttributedString(from: captionText, with: _captionLabel!.font, textColor: Colors.fontNormal(), isOwn: true, application: UIApplication.shared)
  419. let formattedAttributeString = NSMutableAttributedString.init(attributedString: (_captionLabel!.applyMarkup(for: attributed))!)
  420. _captionLabel?.attributedText = TextStyleUtils.makeMentionsAttributedString(for: formattedAttributeString, textFont: _captionLabel!.font!, at: _captionLabel!.textColor.withAlphaComponent(0.4), messageInfo: Int32(message.isOwn!.intValue), application: UIApplication.shared)
  421. _captionLabel?.isHidden = false
  422. }
  423. else {
  424. _captionLabel?.text = nil
  425. _captionLabel?.isHidden = true
  426. }
  427. if fileMessage.data != nil, fileMessage.data.data != nil {
  428. setupAnimatedImage()
  429. _downloadBackground?.isHidden = true
  430. } else {
  431. _downloadSizeLabel?.text = Utils.formatDataLength(CGFloat(fileMessage.fileSize!.floatValue))
  432. _downloadBackground?.isHidden = false
  433. }
  434. enableAnimation(true)
  435. setupColors()
  436. self.setNeedsLayout()
  437. }
  438. @objc func resendMessage(_ menuController: UIMenuController) {
  439. let fileMessage = message as! FileMessage
  440. let sender: FileMessageSender = FileMessageSender.init()
  441. sender.retryMessage(fileMessage)
  442. }
  443. @objc func speakMessage(_ menuController: UIMenuController) {
  444. if _captionLabel?.text != nil {
  445. let speakText = "\(BundleUtil.localizedString(forKey: "image") ?? "Image"). \(_captionLabel!.text!)"
  446. let utterance: AVSpeechUtterance = AVSpeechUtterance.init(string: speakText)
  447. let syn = AVSpeechSynthesizer.init()
  448. syn.speak(utterance)
  449. }
  450. }
  451. }