Signal-iOS/Signal/src/ViewControllers/ConversationView/ConversationViewController+VoiceMessage.swift
2021-07-07 09:53:09 -03:00

119 lines
4.0 KiB
Swift

//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
extension ConversationViewController {
func checkPermissionsAndStartRecordingVoiceMessage() {
AssertIsOnMainThread()
// Cancel any ongoing audio playback.
cvAudioPlayer.stopAll()
let voiceMessageModel = VoiceMessageModel(thread: thread)
viewState.currentVoiceMessageModel = voiceMessageModel
// Delay showing the voice memo UI for N ms to avoid a jarring transition
// when you just tap and don't hold.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
guard let self = self else { return }
guard self.viewState.currentVoiceMessageModel === voiceMessageModel else { return }
self.configureScrollDownButtons()
self.inputToolbar?.showVoiceMemoUI()
}
ows_askForMicrophonePermissions { [weak self] granted in
guard let self = self else { return }
guard self.viewState.currentVoiceMessageModel === voiceMessageModel else { return }
guard granted else {
self.cancelRecordingVoiceMessage()
self.ows_showNoMicrophonePermissionActionSheet()
return
}
self.startRecordingVoiceMessage()
}
}
private func startRecordingVoiceMessage() {
AssertIsOnMainThread()
guard let voiceMessageModel = viewState.currentVoiceMessageModel else {
return owsFailDebug("Unexpectedly missing voice message model")
}
ImpactHapticFeedback.impactOccured(style: .light)
do {
try voiceMessageModel.startRecording()
} catch {
owsFailDebug("Failed to start recording voice message \(error)")
cancelRecordingVoiceMessage()
}
}
func cancelRecordingVoiceMessage() {
AssertIsOnMainThread()
defer { viewState.currentVoiceMessageModel = nil }
guard let voiceMessageModel = viewState.currentVoiceMessageModel else { return }
voiceMessageModel.stopRecordingAsync()
NotificationHapticFeedback().notificationOccurred(.warning)
clearVoiceMessageDraft()
viewState.currentVoiceMessageModel = nil
inputToolbar?.hideVoiceMemoUI(true)
configureScrollDownButtons()
}
private static let minimumVoiceMessageDuration: TimeInterval = 1
@objc(finishRecordingVoiceMessageAndSendImmediately:)
func finishRecordingVoiceMessage(sendImmediately: Bool = false) {
AssertIsOnMainThread()
defer { viewState.currentVoiceMessageModel = nil }
guard let voiceMessageModel = viewState.currentVoiceMessageModel else { return }
voiceMessageModel.stopRecording()
guard let duration = voiceMessageModel.duration, duration >= Self.minimumVoiceMessageDuration else {
inputToolbar?.showVoiceMemoTooltip()
cancelRecordingVoiceMessage()
return
}
ImpactHapticFeedback.impactOccured(style: .medium)
if sendImmediately {
sendVoiceMessageModel(voiceMessageModel)
} else {
databaseStorage.asyncWrite { voiceMessageModel.saveDraft(transaction: $0) } completion: {
self.inputToolbar?.showVoiceMemoDraft(voiceMessageModel)
self.configureScrollDownButtons()
}
}
}
func sendVoiceMessageModel(_ voiceMessageModel: VoiceMessageModel) {
inputToolbar?.hideVoiceMemoUI(true)
configureScrollDownButtons()
do {
let attachment = try voiceMessageModel.prepareForSending()
tryToSendAttachments([attachment], messageBody: nil)
clearVoiceMessageDraft()
} catch {
owsFailDebug("Failed to send prepare voice message for sending \(error)")
}
}
func clearVoiceMessageDraft() {
databaseStorage.asyncWrite { [thread] in VoiceMessageModel.clearDraft(for: thread, transaction: $0) }
}
}