160 lines
6.3 KiB
Swift
160 lines
6.3 KiB
Swift
//
|
|
// Copyright 2019 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import Lottie
|
|
import SignalServiceKit
|
|
import SignalUI
|
|
|
|
// Defines the look of audio messages in the conversation view.
|
|
class AudioMessagePresenter: AudioPresenter {
|
|
let name = "AudioMessageView"
|
|
let isIncoming: Bool
|
|
let playbackTimeLabel = CVLabel()
|
|
let playedDotContainer = ManualLayoutView(name: "playedDotContainer")
|
|
let playbackRateView: AudioMessagePlaybackRateView
|
|
let audioAttachment: AudioAttachment
|
|
let threadUniqueId: String
|
|
let audioPlaybackRate: AudioPlaybackRate
|
|
|
|
init(isIncoming: Bool,
|
|
audioAttachment: AudioAttachment,
|
|
threadUniqueId: String,
|
|
playbackRate: AudioPlaybackRate) {
|
|
self.threadUniqueId = threadUniqueId
|
|
self.audioPlaybackRate = playbackRate
|
|
self.isIncoming = isIncoming
|
|
self.audioAttachment = audioAttachment
|
|
self.playbackRateView = AudioMessagePlaybackRateView(
|
|
threadUniqueId: threadUniqueId,
|
|
audioAttachment: audioAttachment,
|
|
playbackRate: playbackRate,
|
|
isIncoming: isIncoming)
|
|
}
|
|
|
|
var bottomInnerStackSpacing: CGFloat {
|
|
switch UIApplication.shared.preferredContentSizeCategory {
|
|
case .extraSmall, .small, .medium, .large, .extraLarge:
|
|
return 8
|
|
default:
|
|
return 4
|
|
}
|
|
}
|
|
|
|
func playedColor(isIncoming: Bool) -> UIColor {
|
|
return isIncoming ? (Theme.isDarkThemeEnabled ? .ows_gray15 : .ows_gray60)
|
|
: .ows_white
|
|
}
|
|
|
|
func unplayedColor(isIncoming: Bool) -> UIColor {
|
|
return isIncoming ? (Theme.isDarkThemeEnabled ? .ows_gray60 : .ows_gray25) : .ows_whiteAlpha40
|
|
}
|
|
|
|
func thumbColor(isIncoming: Bool) -> UIColor {
|
|
return playedColor(isIncoming: isIncoming)
|
|
}
|
|
|
|
func playPauseContainerBackgroundColor(isIncoming: Bool) -> UIColor {
|
|
return isIncoming ? (Theme.isDarkThemeEnabled ? .ows_gray60 : .ows_whiteAlpha80) : .ows_whiteAlpha20
|
|
}
|
|
|
|
func playPauseAnimationColor(isIncoming: Bool) -> ColorValueProvider {
|
|
ColorValueProvider(thumbColor(isIncoming: isIncoming).lottieColorValue)
|
|
}
|
|
|
|
func playedDotAnimationColor(conversationStyle: ConversationStyle,
|
|
isIncoming: Bool) -> ColorValueProvider {
|
|
return ColorValueProvider(conversationStyle.bubbleSecondaryTextColor(isIncoming: isIncoming).lottieColorValue)
|
|
}
|
|
|
|
func configureForRendering(conversationStyle: ConversationStyle) {
|
|
let playbackTimeLabelConfig = Self.playbackTimeLabelConfig(isIncoming: isIncoming, conversationStyle: conversationStyle)
|
|
playbackTimeLabelConfig.applyForRendering(label: playbackTimeLabel)
|
|
playbackTimeLabel.setContentHuggingHigh()
|
|
}
|
|
|
|
func bottomSubviewGenerators(conversationStyle: ConversationStyle?) -> [SubviewGenerator] {
|
|
struct SubviewConfig {
|
|
var playbackTimeLabelMeasurementInfo: ManualStackSubviewInfo
|
|
var playedDotContainerMeasurementInfo: ManualStackSubviewInfo
|
|
var playbackRateViewMeasurementInfo: ManualStackSubviewInfo
|
|
}
|
|
let makeSubviewConfig = { [unowned self] (maxWidth: CGFloat) -> SubviewConfig in
|
|
let dotSize = CGSize(square: 6)
|
|
|
|
let playbackTimeLabelConfig = Self.playbackTimeLabelConfig_forMeasurement(
|
|
audioAttachment: audioAttachment,
|
|
maxWidth: maxWidth
|
|
)
|
|
let playbackTimeLabelSize = CVText.measureLabel(config: playbackTimeLabelConfig, maxWidth: maxWidth)
|
|
|
|
let playbackRateSize = AudioMessagePlaybackRateView.measure(maxWidth: maxWidth)
|
|
|
|
return SubviewConfig(
|
|
playbackTimeLabelMeasurementInfo: playbackTimeLabelSize.asManualSubviewInfo(hasFixedSize: true),
|
|
playedDotContainerMeasurementInfo: dotSize.asManualSubviewInfo(hasFixedSize: true),
|
|
playbackRateViewMeasurementInfo: playbackRateSize.asManualSubviewInfo(hasFixedSize: true))
|
|
}
|
|
var subviewConfig: SubviewConfig?
|
|
let lazySubviewConfig = { maxWidth in
|
|
if let subviewConfig {
|
|
return subviewConfig
|
|
}
|
|
let result = makeSubviewConfig(maxWidth)
|
|
subviewConfig = result
|
|
return result
|
|
}
|
|
|
|
return [
|
|
SubviewGenerator(
|
|
id: "transparentSpacer1",
|
|
measurementInfo: { _ in CGSize.zero.asManualSubviewInfo(hasFixedWidth: true) },
|
|
viewGenerator: { UIView.transparentSpacer() }),
|
|
SubviewGenerator(
|
|
id: "playbackTimeLabel",
|
|
measurementInfo: { lazySubviewConfig($0).playbackTimeLabelMeasurementInfo },
|
|
viewGenerator: { [unowned self] in self.playbackTimeLabel }),
|
|
SubviewGenerator(
|
|
id: "playedDotContainer",
|
|
measurementInfo: { lazySubviewConfig($0).playedDotContainerMeasurementInfo },
|
|
viewGenerator: { [unowned self] in self.playedDotContainer }),
|
|
SubviewGenerator(
|
|
id: "playbackRateView",
|
|
measurementInfo: { lazySubviewConfig($0).playbackRateViewMeasurementInfo },
|
|
viewGenerator: { [unowned self] in self.playbackRateView}),
|
|
SubviewGenerator(
|
|
id: "transparentSpacer2",
|
|
measurementInfo: { _ in .empty },
|
|
viewGenerator: { UIView.transparentSpacer() })
|
|
]
|
|
}
|
|
|
|
func topLabelConfig(audioAttachment: AudioAttachment, isIncoming: Bool, conversationStyle: ConversationStyle?) -> CVLabelConfig? {
|
|
|
|
guard !audioAttachment.isVoiceMessage else {
|
|
return nil
|
|
}
|
|
|
|
let text: String
|
|
if let fileName = audioAttachment.sourceFilename?.stripped, !fileName.isEmpty {
|
|
text = fileName
|
|
} else {
|
|
text = OWSLocalizedString("GENERIC_ATTACHMENT_LABEL", comment: "A label for generic attachments.")
|
|
}
|
|
|
|
return CVLabelConfig.unstyledText(
|
|
text,
|
|
font: Constants.labelFont,
|
|
textColor: conversationStyle?.bubbleTextColor(isIncoming: isIncoming) ?? .label
|
|
)
|
|
}
|
|
|
|
func audioWaveform(attachmentStream: SignalServiceKit.AttachmentStream?) -> Task<AudioWaveform, Error>? {
|
|
return attachmentStream?.audioWaveform()
|
|
}
|
|
}
|
|
|
|
private enum Constants {
|
|
static let labelFont: UIFont = .dynamicTypeCaption2
|
|
}
|