TM-SGNL-iOS/Signal/ConversationView/ConversationViewController+VoiceMessage.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

121 lines
4.1 KiB
Swift

//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
import SignalServiceKit
import SignalUI
extension ConversationViewController {
func checkPermissionsAndStartRecordingVoiceMessage() {
AssertIsOnMainThread()
// Cancel any ongoing audio playback.
AppEnvironment.shared.cvAudioPlayerRef.stopAll()
let inProgressVoiceMessage = VoiceMessageInProgressDraft(
thread: thread,
audioSession: SUIEnvironment.shared.audioSessionRef,
sleepManager: DeviceSleepManager.shared
)
viewState.inProgressVoiceMessage = inProgressVoiceMessage
// 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.inProgressVoiceMessage === inProgressVoiceMessage else { return }
self.configureScrollDownButtons()
self.inputToolbar?.showVoiceMemoUI()
}
ows_askForMicrophonePermissions { [weak self] granted in
guard let self = self else { return }
guard self.viewState.inProgressVoiceMessage === inProgressVoiceMessage else { return }
guard granted else {
self.cancelRecordingVoiceMessage()
self.ows_showNoMicrophonePermissionActionSheet()
return
}
self.startRecordingVoiceMessage(inProgressVoiceMessage)
}
}
private func startRecordingVoiceMessage(_ inProgressVoiceMessage: VoiceMessageInProgressDraft) {
AssertIsOnMainThread()
ImpactHapticFeedback.impactOccurred(style: .light)
do {
try inProgressVoiceMessage.startRecording()
} catch {
owsFailDebug("Failed to start recording voice message \(error)")
cancelRecordingVoiceMessage()
}
}
func cancelRecordingVoiceMessage() {
AssertIsOnMainThread()
viewState.inProgressVoiceMessage?.stopRecordingAsync()
viewState.inProgressVoiceMessage = nil
NotificationHapticFeedback().notificationOccurred(.warning)
clearVoiceMessageDraft()
inputToolbar?.hideVoiceMemoUI(animated: true)
configureScrollDownButtons()
}
private static let minimumVoiceMessageDuration: TimeInterval = 1
func finishRecordingVoiceMessage(sendImmediately: Bool = false) {
AssertIsOnMainThread()
guard let inProgressVoiceMessage = viewState.inProgressVoiceMessage else { return }
viewState.inProgressVoiceMessage = nil
inProgressVoiceMessage.stopRecording()
guard let duration = inProgressVoiceMessage.duration, duration >= Self.minimumVoiceMessageDuration else {
inputToolbar?.showVoiceMemoTooltip()
cancelRecordingVoiceMessage()
return
}
ImpactHapticFeedback.impactOccurred(style: .medium)
configureScrollDownButtons()
if sendImmediately {
sendVoiceMessageDraft(inProgressVoiceMessage)
} else {
SSKEnvironment.shared.databaseStorageRef.asyncWrite {
let interruptedDraft = inProgressVoiceMessage.convertToDraft(transaction: $0)
DispatchQueue.main.async {
self.inputToolbar?.showVoiceMemoDraft(interruptedDraft)
}
}
}
}
func sendVoiceMessageDraft(_ voiceMemoDraft: VoiceMessageSendableDraft) {
inputToolbar?.hideVoiceMemoUI(animated: true)
do {
tryToSendAttachments([try voiceMemoDraft.prepareAttachment()], messageBody: nil)
clearVoiceMessageDraft()
} catch {
owsFailDebug("Failed to send prepare voice message for sending \(error)")
}
}
func clearVoiceMessageDraft() {
SSKEnvironment.shared.databaseStorageRef.asyncWrite { [thread] in
VoiceMessageInterruptedDraftStore.clearDraft(for: thread, transaction: $0)
}
}
}