소스 검색

implement audio messages with attached text descriptions, implement AudioPlayerView

cyberta 5 년 전
부모
커밋
25473a5123

+ 4 - 0
deltachat-ios.xcodeproj/project.pbxproj

@@ -13,6 +13,7 @@
 		3022E6BE22E8768800763272 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3022E6C022E8768800763272 /* InfoPlist.strings */; };
 		3040F45E234DFBC000FA34D5 /* Audio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3040F45D234DFBC000FA34D5 /* Audio.swift */; };
 		3040F460234F419400FA34D5 /* BasicAudioController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3040F45F234F419300FA34D5 /* BasicAudioController.swift */; };
+		3040F462234F550300FA34D5 /* AudioPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3040F461234F550300FA34D5 /* AudioPlayerView.swift */; };
 		305961CC2346125100C80F33 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961822346125000C80F33 /* UIView+Extensions.swift */; };
 		305961CD2346125100C80F33 /* UIEdgeInsets+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961832346125000C80F33 /* UIEdgeInsets+Extensions.swift */; };
 		305961CF2346125100C80F33 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961852346125000C80F33 /* UIColor+Extensions.swift */; };
@@ -178,6 +179,7 @@
 		3022E6D322E876A100763272 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		3040F45D234DFBC000FA34D5 /* Audio.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Audio.swift; sourceTree = "<group>"; };
 		3040F45F234F419300FA34D5 /* BasicAudioController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicAudioController.swift; sourceTree = "<group>"; };
+		3040F461234F550300FA34D5 /* AudioPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerView.swift; sourceTree = "<group>"; };
 		305961822346125000C80F33 /* UIView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = "<group>"; };
 		305961832346125000C80F33 /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Extensions.swift"; sourceTree = "<group>"; };
 		305961852346125000C80F33 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
@@ -484,6 +486,7 @@
 				305961BE2346125100C80F33 /* MessagesCollectionView.swift */,
 				305961BF2346125100C80F33 /* PlayButtonView.swift */,
 				305961C02346125100C80F33 /* BubbleCircle.swift */,
+				3040F461234F550300FA34D5 /* AudioPlayerView.swift */,
 			);
 			path = Views;
 			sourceTree = "<group>";
@@ -1017,6 +1020,7 @@
 				305961EA2346125100C80F33 /* MessageStyle.swift in Sources */,
 				305961F92346125100C80F33 /* MessageLabel.swift in Sources */,
 				305961FA2346125100C80F33 /* MessageReusableView.swift in Sources */,
+				3040F462234F550300FA34D5 /* AudioPlayerView.swift in Sources */,
 				AE52EA19229EB53C00C586C9 /* ContactDetailHeader.swift in Sources */,
 				78E45E4421D3F14A00D4B15E /* UIImage+Extension.swift in Sources */,
 				3040F460234F419400FA34D5 /* BasicAudioController.swift in Sources */,

+ 24 - 13
deltachat-ios/MessageKit/Controllers/BasicAudioController.swift

@@ -83,12 +83,14 @@ open class BasicAudioController: NSObject, AVAudioPlayerDelegate {
     open func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) {
         if playingMessage?.messageId == message.messageId, let collectionView = messageCollectionView, let player = audioPlayer {
             playingCell = cell
-            cell.progressView.progress = (player.duration == 0) ? 0 : Float(player.currentTime/player.duration)
-            cell.playButton.isSelected = (player.isPlaying == true) ? true : false
+            cell.audioPlayerView.setProgress((player.duration == 0) ? 0 : Float(player.currentTime/player.duration))
+            cell.audioPlayerView.showPlayLayout((player.isPlaying == true) ? true : false)
             guard let displayDelegate = collectionView.messagesDisplayDelegate else {
                 fatalError("MessagesDisplayDelegate has not been set.")
             }
-            cell.durationLabel.text = displayDelegate.audioProgressTextFormat(Float(player.currentTime), for: cell, in: collectionView)
+            cell.audioPlayerView.setDuration(formattedText: displayDelegate.audioProgressTextFormat(Float(player.currentTime),
+                                                                                                    for: cell,
+                                                                                                    in: collectionView))
         }
     }
 
@@ -111,7 +113,7 @@ open class BasicAudioController: NSObject, AVAudioPlayerDelegate {
             audioPlayer?.delegate = self
             audioPlayer?.play()
             state = .playing
-            audioCell.playButton.isSelected = true  // show pause button on audio cell
+            audioCell.audioPlayerView.showPlayLayout(true)  // show pause button on audio cell
             startProgressTimer()
             audioCell.delegate?.didStartAudio(in: audioCell)
         default:
@@ -127,7 +129,7 @@ open class BasicAudioController: NSObject, AVAudioPlayerDelegate {
     open func pauseSound(for message: MessageType, in audioCell: AudioMessageCell) {
         audioPlayer?.pause()
         state = .pause
-        audioCell.playButton.isSelected = false // show play button on audio cell
+        audioCell.audioPlayerView.showPlayLayout(false) // show play button on audio cell
         progressTimer?.invalidate()
         if let cell = playingCell {
             cell.delegate?.didPauseAudio(in: cell)
@@ -136,16 +138,19 @@ open class BasicAudioController: NSObject, AVAudioPlayerDelegate {
 
     /// Stops any ongoing audio playing if exists
     open func stopAnyOngoingPlaying() {
-        guard let player = audioPlayer, let collectionView = messageCollectionView else { return } // If the audio player is nil then we don't need to go through the stopping logic
+        // If the audio player is nil then we don't need to go through the stopping logic
+        guard let player = audioPlayer, let collectionView = messageCollectionView else { return }
         player.stop()
         state = .stopped
         if let cell = playingCell {
-            cell.progressView.progress = 0.0
-            cell.playButton.isSelected = false
+            cell.audioPlayerView.setProgress(0.0)
+            cell.audioPlayerView.showPlayLayout(false)
             guard let displayDelegate = collectionView.messagesDisplayDelegate else {
                 fatalError("MessagesDisplayDelegate has not been set.")
             }
-            cell.durationLabel.text = displayDelegate.audioProgressTextFormat(Float(player.duration), for: cell, in: collectionView)
+            cell.audioPlayerView.setDuration(formattedText: displayDelegate.audioProgressTextFormat(Float(player.duration),
+                                                                                                    for: cell,
+                                                                                                    in: collectionView))
             cell.delegate?.didStopAudio(in: cell)
         }
         progressTimer?.invalidate()
@@ -165,7 +170,7 @@ open class BasicAudioController: NSObject, AVAudioPlayerDelegate {
         player.play()
         state = .playing
         startProgressTimer()
-        cell.playButton.isSelected = true // show pause button on audio cell
+        cell.audioPlayerView.showPlayLayout(true) // show pause button on audio cell
         cell.delegate?.didStartAudio(in: cell)
     }
 
@@ -182,11 +187,13 @@ open class BasicAudioController: NSObject, AVAudioPlayerDelegate {
             let currentMessage = collectionView.messagesDataSource?.messageForItem(at: playingCellIndexPath, in: collectionView)
             if currentMessage != nil && currentMessage?.messageId == playingMessage?.messageId {
                 // messages are the same update cell content
-                cell.progressView.progress = (player.duration == 0) ? 0 : Float(player.currentTime/player.duration)
+                cell.audioPlayerView.setProgress((player.duration == 0) ? 0 : Float(player.currentTime/player.duration))
                 guard let displayDelegate = collectionView.messagesDisplayDelegate else {
                     fatalError("MessagesDisplayDelegate has not been set.")
                 }
-                cell.durationLabel.text = displayDelegate.audioProgressTextFormat(Float(player.currentTime), for: cell, in: collectionView)
+                cell.audioPlayerView.setDuration(formattedText: displayDelegate.audioProgressTextFormat(Float(player.currentTime),
+                                                                                                        for: cell,
+                                                                                                        in: collectionView))
             } else {
                 // if the current message is not the same with playing message stop playing sound
                 stopAnyOngoingPlaying()
@@ -198,7 +205,11 @@ open class BasicAudioController: NSObject, AVAudioPlayerDelegate {
     private func startProgressTimer() {
         progressTimer?.invalidate()
         progressTimer = nil
-        progressTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(BasicAudioController.didFireProgressTimer(_:)), userInfo: nil, repeats: true)
+        progressTimer = Timer.scheduledTimer(timeInterval: 0.1,
+                                             target: self,
+                                             selector: #selector(BasicAudioController.didFireProgressTimer(_:)),
+                                             userInfo: nil,
+                                             repeats: true)
     }
 
     // MARK: - AVAudioPlayerDelegate

+ 50 - 4
deltachat-ios/MessageKit/Layout/AudioMessageSizeCalculator.swift

@@ -27,18 +27,64 @@ import UIKit
 
 open class AudioMessageSizeCalculator: MessageSizeCalculator {
 
+    public var incomingMessageLabelInsets = UIEdgeInsets(top: AudioMessageCell.insetTop,
+                                                         left: AudioMessageCell.insetHorizontalBig,
+                                                         bottom: AudioMessageCell.insetBottom,
+                                                         right: AudioMessageCell.insetHorizontalSmall)
+   public var outgoingMessageLabelInsets = UIEdgeInsets(top: AudioMessageCell.insetTop,
+                                                        left: AudioMessageCell.insetHorizontalSmall,
+                                                        bottom: AudioMessageCell.insetBottom,
+                                                        right: AudioMessageCell.insetHorizontalBig)
+
+    public var messageLabelFont = UIFont.preferredFont(forTextStyle: .body)
+
+
+    internal func messageLabelInsets(for message: MessageType) -> UIEdgeInsets {
+        let dataSource = messagesLayout.messagesDataSource
+        let isFromCurrentSender = dataSource.isFromCurrentSender(message: message)
+        return isFromCurrentSender ? outgoingMessageLabelInsets : incomingMessageLabelInsets
+    }
+
     open override func messageContainerSize(for message: MessageType) -> CGSize {
         switch message.kind {
         case .audio(let item):
             let maxWidth = messageContainerMaxWidth(for: message)
-            if maxWidth < item.size.width {
+            var itemWidth = item.size.width
+            var itemHeight = item.size.height
+
+            if maxWidth < itemWidth {
                 // Maintain the ratio if width is too great
-                let height = maxWidth * item.size.height / item.size.width
-                return CGSize(width: maxWidth, height: height)
+                itemHeight = maxWidth * item.size.height / item.size.width
+                itemWidth = maxWidth
+            }
+
+            let maxTextWidth = itemWidth - self.messageLabelInsets(for: message).horizontal
+            var messageContainerSize = CGSize(width: itemWidth, height: itemHeight)
+            if let text = item.text {
+                let textHeight = text.height(withConstrainedWidth: maxTextWidth)
+                messageContainerSize.height += textHeight
+                messageContainerSize.height +=  self.messageLabelInsets(for: message).vertical
             }
-            return item.size
+            return messageContainerSize
         default:
             fatalError("messageContainerSize received unhandled MessageDataType: \(message.kind)")
         }
     }
+
+    open override func configure(attributes: UICollectionViewLayoutAttributes) {
+        super.configure(attributes: attributes)
+        guard let attributes = attributes as? MessagesCollectionViewLayoutAttributes else { return }
+
+        let dataSource = messagesLayout.messagesDataSource
+        let indexPath = attributes.indexPath
+        let message = dataSource.messageForItem(at: indexPath, in: messagesLayout.messagesCollectionView)
+
+        switch message.kind {
+        case .audio:
+            attributes.messageLabelInsets = messageLabelInsets(for: message)
+            attributes.messageLabelFont = messageLabelFont
+        default:
+            break
+        }
+    }
 }

+ 118 - 0
deltachat-ios/MessageKit/Views/AudioPlayerView.swift

@@ -0,0 +1,118 @@
+//
+//  AudioPlayerView.swift
+//  deltachat-ios
+//
+//  Created by Macci on 10.10.19.
+//  Copyright © 2019 Jonas Reinsch. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+open class AudioPlayerView: UIView {
+    //open weak var playerDelegate: AudioPlayerDelegate?
+
+    /// The play button view to display on audio messages.
+    private lazy var playButton: UIButton = {
+        let playButton = UIButton(type: .custom)
+        let playImage = UIImage(named: "play")
+        let pauseImage = UIImage(named: "pause")
+        playButton.setImage(playImage?.withRenderingMode(.alwaysTemplate), for: .normal)
+        playButton.setImage(pauseImage?.withRenderingMode(.alwaysTemplate), for: .selected)
+        playButton.translatesAutoresizingMaskIntoConstraints = false
+        return playButton
+    }()
+
+    /// The time duration lable to display on audio messages.
+    private lazy var durationLabel: UILabel = {
+        let durationLabel = UILabel(frame: CGRect.zero)
+        durationLabel.textAlignment = .right
+        durationLabel.font = UIFont.systemFont(ofSize: 14)
+        durationLabel.text = "0:00"
+        durationLabel.translatesAutoresizingMaskIntoConstraints = false
+        return durationLabel
+    }()
+
+    private lazy var progressView: UIProgressView = {
+        let progressView = UIProgressView(progressViewStyle: .default)
+        progressView.progress = 0.0
+        progressView.translatesAutoresizingMaskIntoConstraints = false
+        return progressView
+    }()
+
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+        setupSubviews()
+    }
+
+    public required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+        self.translatesAutoresizingMaskIntoConstraints = false
+        setupSubviews()
+    }
+
+    /// Responsible for setting up the constraints of the cell's subviews.
+    open func setupConstraints() {
+        playButton.constraint(equalTo: CGSize(width: 35, height: 35))
+
+        let playButtonConstraints = [playButton.constraintCenterYTo(self),
+                                     playButton.constraintAlignLeadingTo(self, paddingLeading: 12)]
+        let durationLabelConstraints = [durationLabel.constraintAlignTrailingTo(self, paddingTrailing: 12),
+                                        durationLabel.constraintCenterYTo(self)]
+        self.addConstraints(playButtonConstraints)
+        self.addConstraints(durationLabelConstraints)
+
+        progressView.addConstraints(left: playButton.rightAnchor,
+                                    right: durationLabel.leftAnchor,
+                                    centerY: self.centerYAnchor,
+                                    leftConstant: 8,
+                                    rightConstant: 8)
+    }
+
+    open func setupSubviews() {
+        self.addSubview(playButton)
+        self.addSubview(durationLabel)
+        self.addSubview(progressView)
+        setupConstraints()
+    }
+
+    open func reset() {
+        progressView.progress = 0
+        playButton.isSelected = false
+        durationLabel.text = "0:00"
+    }
+
+    open func didTapPlayButton(_ gesture: UIGestureRecognizer) -> Bool {
+        let touchLocation = gesture.location(in: self)
+        // compute play button touch area, currently play button size is (25, 25) which is hardly touchable
+        // add 10 px around current button frame and test the touch against this new frame
+        let playButtonTouchArea = CGRect(playButton.frame.origin.x - 10.0,
+                                         playButton.frame.origin.y - 10,
+                                         playButton.frame.size.width + 20,
+                                         playButton.frame.size.height + 20)
+        let translateTouchLocation = convert(touchLocation, to: self)
+        if playButtonTouchArea.contains(translateTouchLocation) {
+            return true
+        } else {
+            return false
+        }
+    }
+
+    open func setTintColor(_ color: UIColor) {
+        playButton.imageView?.tintColor = tintColor
+        durationLabel.textColor = tintColor
+        progressView.tintColor = tintColor
+    }
+
+    open func setProgress(_ progress: Float) {
+        progressView.progress = progress
+    }
+
+    open func setDuration(formattedText: String) {
+        durationLabel.text = formattedText
+    }
+
+    open func showPlayLayout(_ play: Bool) {
+        playButton.isSelected = play
+    }
+}

+ 88 - 64
deltachat-ios/MessageKit/Views/Cells/AudioMessageCell.swift

@@ -28,71 +28,72 @@ import AVFoundation
 /// A subclass of `MessageContentCell` used to display video and audio messages.
 open class AudioMessageCell: MessageContentCell {
 
-    /// The play button view to display on audio messages.
-    public lazy var playButton: UIButton = {
-        let playButton = UIButton(type: .custom)
-        let playImage = UIImage(named: "play")
-        let pauseImage = UIImage(named: "pause")
-        playButton.setImage(playImage?.withRenderingMode(.alwaysTemplate), for: .normal)
-        playButton.setImage(pauseImage?.withRenderingMode(.alwaysTemplate), for: .selected)
-        return playButton
-    }()
+    public static let insetTop: CGFloat = 12
+    public static let insetBottom: CGFloat = 12
+    public static let insetHorizontalBig: CGFloat = 23
+    public static let insetHorizontalSmall: CGFloat = 12
+
+    // MARK: - Properties
+    /// The `MessageCellDelegate` for the cell.
+    open override weak var delegate: MessageCellDelegate? {
+        didSet {
+            messageLabel.delegate = delegate
+        }
+    }
 
-    /// The time duration lable to display on audio messages.
-    public lazy var durationLabel: UILabel = {
-        let durationLabel = UILabel(frame: CGRect.zero)
-        durationLabel.textAlignment = .right
-        durationLabel.font = UIFont.systemFont(ofSize: 14)
-        durationLabel.text = "0:00"
-        return durationLabel
-    }()
+    /// The label used to display the message's text.
+    open var messageLabel = MessageLabel()
 
-    public lazy var progressView: UIProgressView = {
-        let progressView = UIProgressView(progressViewStyle: .default)
-        progressView.progress = 0.0
-        return progressView
+    public lazy var audioPlayerView: AudioPlayerView = {
+        let audioPlayerView = AudioPlayerView()
+        audioPlayerView.translatesAutoresizingMaskIntoConstraints = false
+        return audioPlayerView
     }()
 
     // MARK: - Methods
-
     /// Responsible for setting up the constraints of the cell's subviews.
     open func setupConstraints() {
-        playButton.constraint(equalTo: CGSize(width: 35, height: 35))
-        playButton.addConstraints(left: messageContainerView.leftAnchor, centerY: messageContainerView.centerYAnchor, leftConstant: 5)
-        durationLabel.addConstraints(right: messageContainerView.rightAnchor, centerY: messageContainerView.centerYAnchor, rightConstant: 15)
-        progressView.addConstraints(left: playButton.rightAnchor,
-                                    right: durationLabel.leftAnchor,
-                                    centerY: messageContainerView.centerYAnchor,
-                                    leftConstant: 5,
-                                    rightConstant: 5)
+        messageContainerView.removeConstraints(messageContainerView.constraints)
+        let audioPlayerHeight = messageContainerView.frame.height - getMessageLabelHeight()
+        let audioPlayerConstraints = [ audioPlayerView.constraintHeightTo(audioPlayerHeight),
+                                       audioPlayerView.constraintAlignLeadingTo(messageContainerView),
+                                       audioPlayerView.constraintAlignTrailingTo(messageContainerView),
+                                       audioPlayerView.constraintAlignTopTo(messageContainerView)
+        ]
+        messageContainerView.addConstraints(audioPlayerConstraints)
+
+        messageLabel.frame = CGRect(x: 0,
+                                    y: messageContainerView.frame.height - getMessageLabelHeight(),
+                                    width: messageContainerView.frame.width,
+                                    height: getMessageLabelHeight())
+    }
+
+    func getMessageLabelHeight() -> CGFloat {
+        if let text = messageLabel.attributedText {
+            let height = (text.height(withConstrainedWidth:
+                messageContainerView.frame.width -
+                    TextMediaMessageCell.insetHorizontalSmall -
+                    TextMediaMessageCell.insetHorizontalBig))
+            return height + TextMediaMessageCell.insetBottom + TextMediaMessageCell.insetTop
+        }
+        return 0
     }
 
     open override func setupSubviews() {
         super.setupSubviews()
-        messageContainerView.addSubview(playButton)
-        messageContainerView.addSubview(durationLabel)
-        messageContainerView.addSubview(progressView)
-        setupConstraints()
+        messageContainerView.addSubview(audioPlayerView)
+        messageContainerView.addSubview(messageLabel)
     }
 
     open override func prepareForReuse() {
         super.prepareForReuse()
-        progressView.progress = 0
-        playButton.isSelected = false
-        durationLabel.text = "0:00"
+        audioPlayerView.reset()
+        messageLabel.attributedText = nil
     }
 
     /// Handle tap gesture on contentView and its subviews.
     open override func handleTapGesture(_ gesture: UIGestureRecognizer) {
-        let touchLocation = gesture.location(in: self)
-        // compute play button touch area, currently play button size is (25, 25) which is hardly touchable
-        // add 10 px around current button frame and test the touch against this new frame
-        let playButtonTouchArea = CGRect(playButton.frame.origin.x - 10.0,
-                                         playButton.frame.origin.y - 10,
-                                         playButton.frame.size.width + 20,
-                                         playButton.frame.size.height + 20)
-        let translateTouchLocation = convert(touchLocation, to: messageContainerView)
-        if playButtonTouchArea.contains(translateTouchLocation) {
+        if audioPlayerView.didTapPlayButton(gesture) {
             delegate?.didTapPlayButton(in: self)
         } else {
             super.handleTapGesture(gesture)
@@ -100,38 +101,61 @@ open class AudioMessageCell: MessageContentCell {
     }
 
     // MARK: - Configure Cell
+    open override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
+           super.apply(layoutAttributes)
+           if let attributes = layoutAttributes as? MessagesCollectionViewLayoutAttributes {
+               messageLabel.textInsets = attributes.messageLabelInsets
+               messageLabel.messageLabelFont = attributes.messageLabelFont
+           }
+       }
 
     open override func configure(with message: MessageType, at indexPath: IndexPath, and messagesCollectionView: MessagesCollectionView) {
         super.configure(with: message, at: indexPath, and: messagesCollectionView)
 
-        guard let dataSource = messagesCollectionView.messagesDataSource else {
+        guard messagesCollectionView.messagesDataSource != nil else {
             fatalError(MessageKitError.nilMessagesDataSource)
         }
 
-        let playButtonLeftConstraint = messageContainerView.constraints.filter { $0.identifier == "left" }.first
-        let durationLabelRightConstraint = messageContainerView.constraints.filter { $0.identifier == "right" }.first
-
-        if !dataSource.isFromCurrentSender(message: message) {
-            playButtonLeftConstraint?.constant = 12
-            durationLabelRightConstraint?.constant = -8
-        } else {
-            playButtonLeftConstraint?.constant = 5
-            durationLabelRightConstraint?.constant = -15
-        }
-
         guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
             fatalError(MessageKitError.nilMessagesDisplayDelegate)
         }
 
         let tintColor = displayDelegate.audioTintColor(for: message, at: indexPath, in: messagesCollectionView)
-        playButton.imageView?.tintColor = tintColor
-        durationLabel.textColor = tintColor
-        progressView.tintColor = tintColor
-
-        displayDelegate.configureAudioCell(self, message: message)
+        audioPlayerView.setTintColor(tintColor)
 
         if case let .audio(audioItem) = message.kind {
-            durationLabel.text = displayDelegate.audioProgressTextFormat(audioItem.duration, for: self, in: messagesCollectionView)
+            audioPlayerView.setDuration(formattedText: displayDelegate.audioProgressTextFormat(audioItem.duration,
+                                                                                               for: self,
+                                                                                               in: messagesCollectionView))
+            configureMessageLabel(for: audioItem,
+                                  with: displayDelegate,
+                                  message: message,
+                                  at: indexPath, in: messagesCollectionView)
         }
+
+        setupConstraints()
+        displayDelegate.configureAudioCell(self, message: message)
+    }
+
+    func configureMessageLabel(for audioItem: AudioItem,
+                               with displayDelegate: MessagesDisplayDelegate,
+                               message: MessageType,
+                               at indexPath: IndexPath,
+                               in messagesCollectionView: MessagesCollectionView) {
+           let enabledDetectors = displayDelegate.enabledDetectors(for: message, at: indexPath, in: messagesCollectionView)
+           messageLabel.configure {
+              messageLabel.enabledDetectors = enabledDetectors
+              for detector in enabledDetectors {
+                  let attributes = displayDelegate.detectorAttributes(for: detector, and: message, at: indexPath)
+                  messageLabel.setAttributes(attributes, detector: detector)
+              }
+               messageLabel.attributedText = audioItem.text
+           }
+       }
+
+    /// Used to handle the cell's contentView's tap gesture.
+    /// Return false when the contentView does not need to handle the gesture.
+    open override func cellContentView(canHandle touchPoint: CGPoint) -> Bool {
+        return messageLabel.handleGesture(touchPoint)
     }
 }

+ 1 - 1
deltachat-ios/Model/Audio.swift

@@ -3,7 +3,7 @@ import Foundation
 import UIKit
 
 struct Audio: AudioItem {
-    var size: CGSize = CGSize(width: 250, height: 100)
+    var size: CGSize = CGSize(width: 250, height: 50)
 
     var url: URL