From c726ef60ddbbb7149634d3d48eaf1a04d6aa0a77 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 29 Jul 2021 16:58:40 +0200 Subject: [PATCH 01/13] Prepare for new sprint --- CHANGES.rst | 24 ++++++++++++++++++++++++ Config/AppIdentifiers.xcconfig | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index bf77aedef..c7d5e0920 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,27 @@ +Changes to be released in next version +================================================= + +✨ Features + * + +🙌 Improvements + * + +🐛 Bugfix + * + +⚠️ API Changes + * + +🗣 Translations + * + +🧱 Build + * + +Others + * + Changes in 1.4.8 (2021-07-29) ================================================= diff --git a/Config/AppIdentifiers.xcconfig b/Config/AppIdentifiers.xcconfig index 1628e83d8..c21273565 100644 --- a/Config/AppIdentifiers.xcconfig +++ b/Config/AppIdentifiers.xcconfig @@ -22,8 +22,8 @@ APPLICATION_GROUP_IDENTIFIER = group.im.vector APPLICATION_SCHEME = element // Version -MARKETING_VERSION = 1.4.8 -CURRENT_PROJECT_VERSION = 1.4.8 +MARKETING_VERSION = 1.4.9 +CURRENT_PROJECT_VERSION = 1.4.9 // Team From eb030bcbf883c03cbec5c4af1f880de45724feb1 Mon Sep 17 00:00:00 2001 From: random Date: Fri, 30 Jul 2021 12:52:28 +0000 Subject: [PATCH 02/13] Translated using Weblate (Italian) Currently translated at 100.0% (6 of 6 strings) Translation: Element iOS/Element iOS (Dialogs) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/it/ --- Riot/Assets/it.lproj/InfoPlist.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/it.lproj/InfoPlist.strings b/Riot/Assets/it.lproj/InfoPlist.strings index d46035a77..8e49c5075 100644 --- a/Riot/Assets/it.lproj/InfoPlist.strings +++ b/Riot/Assets/it.lproj/InfoPlist.strings @@ -1,7 +1,7 @@ // Permissions usage explanations "NSCameraUsageDescription" = "La fotocamera viene utilizzata per scattare fotografie, registrare video ed eseguire videochiamate."; "NSPhotoLibraryUsageDescription" = "La libreria fotografica viene utilizzata per inviare foto e video."; -"NSMicrophoneUsageDescription" = "Il microfono viene utilizzato per registrare video ed effettuare chiamate."; +"NSMicrophoneUsageDescription" = "Element ha bisogno di accedere al microfono per effettuare e ricevere chiamate, registrare video e messaggi vocali."; "NSContactsUsageDescription" = "Per scoprire i contatti che già usano Matrix, Element può inviare gli indirizzi email e i numeri di telefono della tua rubrica al server identità che hai scelto. Se supportato, viene fatto un hash dei dati personali prima dell'invio - controlla la politica sulla privacy del tuo server di identità per maggiori informazioni."; "NSCalendarsUsageDescription" = "Vedi le tue riunioni programmate nell'app."; "NSFaceIDUsageDescription" = "Face ID viene usato per accedere all'app."; From 48cf5b2173b58106cea93180f3c532c389b5a7aa Mon Sep 17 00:00:00 2001 From: sr093906 Date: Thu, 29 Jul 2021 13:45:50 +0000 Subject: [PATCH 03/13] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (1247 of 1247 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/zh_Hans/ --- Riot/Assets/zh_Hans.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 38f1729f1..e8b39a3c8 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -1426,7 +1426,7 @@ "settings_ui_theme_picker_message_invert_colours" = "“自动”使用您设备的“反转颜色”设置"; "room_recents_unknown_room_error_message" = "找不到这个房间。 确保它存在"; "room_creation_dm_error" = "我们无法创建您的 DM。 请检查您要邀请的用户,然后重试。"; -"voice_message_stop_locked_mode_recording" = "轻按波长停止和回放消息"; +"voice_message_stop_locked_mode_recording" = "轻按录音停止或收听"; "voice_message_remaining_recording_time" = "剩 %@s"; // Mark: - Voice Messages From 098d33a7f5a06c57fbf2ba7764caeba77140330e Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 29 Jul 2021 20:24:33 +0000 Subject: [PATCH 04/13] Translated using Weblate (Hungarian) Currently translated at 100.0% (1247 of 1247 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 3e991b86a..fa8d1a78a 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -1424,7 +1424,7 @@ // Room Notification Settings "room_notifs_settings_notify_me_for" = "Értesítés ezért:"; "room_details_notifs" = "Értesítések"; -"voice_message_stop_locked_mode_recording" = "Megállításhoz és visszajátszáshoz koppints a hullámhosszra"; +"voice_message_stop_locked_mode_recording" = "Megállításhoz és visszajátszáshoz koppints a felvételre"; "voice_message_remaining_recording_time" = "%@s távozott"; // Mark: - Voice Messages From 880042665cdaf0391b88472151e50106381ad89d Mon Sep 17 00:00:00 2001 From: random Date: Fri, 30 Jul 2021 12:51:07 +0000 Subject: [PATCH 05/13] Translated using Weblate (Italian) Currently translated at 100.0% (1247 of 1247 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 0a7d64e0e..51cb9ed06 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -1395,3 +1395,10 @@ // Room Notification Settings "room_notifs_settings_notify_me_for" = "Inviami notifiche per"; "room_details_notifs" = "Notifiche"; +"voice_message_stop_locked_mode_recording" = "Tocca la registrazione per fermare o ascoltare"; +"voice_message_remaining_recording_time" = "%@s rimasti"; + +// Mark: - Voice Messages + +"voice_message_release_to_send" = "Tieni premuto per registrare, rilascia per inviare"; +"settings_labs_voice_messages" = "Messaggi vocali"; From 695d66ab55e512853cee4b0bd49bfa5d9d6d129c Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Thu, 29 Jul 2021 14:06:54 +0000 Subject: [PATCH 06/13] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (1247 of 1247 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ --- Riot/Assets/pt_BR.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 626dbaa96..b8eb3fc94 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -1393,7 +1393,7 @@ "room_notifs_settings_notify_me_for" = "Notifique-me para"; "room_details_notifs" = "Notificações"; "voice_message_remaining_recording_time" = "%@s restando"; -"voice_message_stop_locked_mode_recording" = "Toque no comprimento de onda para parar e dar playback"; +"voice_message_stop_locked_mode_recording" = "Toque em sua gravação para parar ou escutar"; // Mark: - Voice Messages From bcdd7813c1af17a7cc4f85fe24f178327ca626da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Sun, 1 Aug 2021 18:21:59 +0000 Subject: [PATCH 07/13] Translated using Weblate (Estonian) Currently translated at 100.0% (1247 of 1247 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 1349ade0d..1d3a38634 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -1361,7 +1361,7 @@ // Room Notification Settings "room_notifs_settings_notify_me_for" = "Teavita mind"; "room_details_notifs" = "Teavitused"; -"voice_message_stop_locked_mode_recording" = "Salvestuse peatamiseks ja taasesituseks vajuta lainekese nuppu"; +"voice_message_stop_locked_mode_recording" = "Salvestuse peatamiseks ja taasesituseks vajuta salvestuse vaadet"; "voice_message_remaining_recording_time" = "salvestusaega jäänud %@s"; // Mark: - Voice Messages From 505472e099760dcad22440d3f78d11edd1a89877 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 3 Aug 2021 11:28:58 +0300 Subject: [PATCH 08/13] #4641 - Voice messages - always access local cache manager state from the internal work queue. --- .../VoiceMessageAttachmentCacheManager.swift | 176 +++++++++--------- 1 file changed, 87 insertions(+), 89 deletions(-) diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift index 73d4df484..f587e0b2d 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift @@ -84,6 +84,7 @@ class VoiceMessageAttachmentCacheManager { workQueue.async { // Run this in the work queue to preserve order if let finalURL = self.finalURLs[identifier], let duration = self.durations[identifier], let samples = self.samples[identifier]?[numberOfSamples] { + MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished task - using cached results") let result = VoiceMessageAttachmentCacheManagerLoadResult(eventIdentifier: identifier, url: finalURL, duration: duration, samples: samples) DispatchQueue.main.async { completion(Result.success(result)) @@ -109,22 +110,93 @@ class VoiceMessageAttachmentCacheManager { completionCallbacks[callbackKey] = [CompletionWrapper(completion)] } - let dispatchGroup = DispatchGroup() + if let finalURL = finalURLs[identifier], let duration = durations[identifier] { + sampleFileAtURL(finalURL, duration: duration, numberOfSamples: numberOfSamples, identifier: identifier) + return + } - func sampleFileAtURL(_ url: URL, duration: TimeInterval) { - let analyser = WaveformAnalyzer(audioAssetURL: url) - - dispatchGroup.enter() - analyser?.samples(count: numberOfSamples, completionHandler: { samples in - MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished sampling voice message") - - dispatchGroup.leave() - + DispatchQueue.main.async { // These don't behave accordingly if called from a background thread + if attachment.isEncrypted { + attachment.decrypt(toTempFile: { filePath in + self.workQueue.async { + self.convertFileAtPath(filePath, numberOfSamples: numberOfSamples, identifier: identifier) + } + }, failure: { error in + // A nil error in this case is a cancellation on the MXMediaLoader + if let error = error { + MXLog.error("Failed decrypting attachment with error: \(String(describing: error))") + self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.decryptionError(error)) + } + }) + } else { + attachment.prepare({ + self.workQueue.async { + self.convertFileAtPath(attachment.cacheFilePath, numberOfSamples: numberOfSamples, identifier: identifier) + } + }, failure: { error in + // A nil error in this case is a cancellation on the MXMediaLoader + if let error = error { + MXLog.error("Failed preparing attachment with error: \(String(describing: error))") + self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.preparationError(error)) + } + }) + } + } + } + + private func convertFileAtPath(_ path: String?, numberOfSamples: Int, identifier: String) { + guard let filePath = path else { + return + } + + let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + let newURL = temporaryDirectoryURL.appendingPathComponent(ProcessInfo().globallyUniqueString).appendingPathExtension("m4a") + + VoiceMessageAudioConverter.convertToMPEG4AAC(sourceURL: URL(fileURLWithPath: filePath), destinationURL: newURL) { result in + MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished converting voice message") + self.workQueue.async { + switch result { + case .success: + self.finalURLs[identifier] = newURL + + VoiceMessageAudioConverter.mediaDurationAt(newURL) { result in + self.workQueue.async { + MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished retrieving media duration") + + switch result { + case .success: + if let duration = try? result.get() { + self.durations[identifier] = duration + self.sampleFileAtURL(newURL, duration: duration, numberOfSamples: numberOfSamples, identifier: identifier) + } else { + MXLog.error("[VoiceMessageAttachmentCacheManager] Failed retrieving media duration") + } + case .failure(let error): + MXLog.error("[VoiceMessageAttachmentCacheManager] Failed retrieving audio duration with error: \(error)") + } + } + } + case .failure(let error): + MXLog.error("[VoiceMessageAttachmentCacheManager] Failed decoding audio message with error: \(error)") + self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.conversionError(error)) + } + } + } + } + + private func sampleFileAtURL(_ url: URL, duration: TimeInterval, numberOfSamples: Int, identifier: String) { + let analyser = WaveformAnalyzer(audioAssetURL: url) + + analyser?.samples(count: numberOfSamples, completionHandler: { samples in + self.workQueue.async { guard let samples = samples else { + MXLog.debug("[VoiceMessageAttachmentCacheManager] Failed sampling voice message") self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.samplingError) return } + MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished sampling voice message") + if var existingSamples = self.samples[identifier] { existingSamples[numberOfSamples] = samples self.samples[identifier] = existingSamples @@ -133,86 +205,8 @@ class VoiceMessageAttachmentCacheManager { } self.invokeSuccessCallbacksForIdentifier(identifier, url: url, duration: duration, samples: samples) - }) - } - - if let finalURL = finalURLs[identifier], let duration = durations[identifier] { - sampleFileAtURL(finalURL, duration: duration) - dispatchGroup.wait() - MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished task") - return - } - - func convertFileAtPath(_ path: String?) { - guard let filePath = path else { - return } - - let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) - let newURL = temporaryDirectoryURL.appendingPathComponent(ProcessInfo().globallyUniqueString).appendingPathExtension("m4a") - - dispatchGroup.enter() - VoiceMessageAudioConverter.convertToMPEG4AAC(sourceURL: URL(fileURLWithPath: filePath), destinationURL: newURL) { result in - switch result { - case .success: - self.finalURLs[identifier] = newURL - VoiceMessageAudioConverter.mediaDurationAt(newURL) { result in - MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished converting voice message") - - switch result { - case .success: - if let duration = try? result.get() { - self.durations[identifier] = duration - sampleFileAtURL(newURL, duration: duration) - } else { - MXLog.error("[VoiceMessageAttachmentCacheManager] enqueueLoadAttachment: Failed to retrieve media duration") - } - case .failure(let error): - MXLog.error("[VoiceMessageAttachmentCacheManager] enqueueLoadAttachment: failed getting audio duration with: \(error)") - } - - dispatchGroup.leave() - } - case .failure(let error): - self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.conversionError(error)) - MXLog.error("[VoiceMessageAttachmentCacheManager] enqueueLoadAttachment: failed decoding audio message with: \(error)") - dispatchGroup.leave() - } - } - } - - dispatchGroup.enter() - DispatchQueue.main.async { // These don't behave accordingly if called from a background thread - if attachment.isEncrypted { - attachment.decrypt(toTempFile: { filePath in - convertFileAtPath(filePath) - dispatchGroup.leave() - }, failure: { error in - // A nil error in this case is a cancellation on the MXMediaLoader - if let error = error { - MXLog.error("Failed decrypting attachment with error: \(String(describing: error))") - self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.decryptionError(error)) - } - dispatchGroup.leave() - }) - } else { - attachment.prepare({ - convertFileAtPath(attachment.cacheFilePath) - dispatchGroup.leave() - }, failure: { error in - // A nil error in this case is a cancellation on the MXMediaLoader - if let error = error { - MXLog.error("Failed preparing attachment with error: \(String(describing: error))") - self.invokeFailureCallbacksForIdentifier(identifier, error: VoiceMessageAttachmentCacheManagerError.preparationError(error)) - } - dispatchGroup.leave() - }) - } - } - - dispatchGroup.wait() - - MXLog.debug("[VoiceMessageAttachmentCacheManager] Finished task") + }) } private func invokeSuccessCallbacksForIdentifier(_ identifier: String, url: URL, duration: TimeInterval, samples: [Float]) { @@ -232,6 +226,8 @@ class VoiceMessageAttachmentCacheManager { } self.completionCallbacks[callbackKey] = nil + + MXLog.debug("[VoiceMessageAttachmentCacheManager] Successfully finished task") } private func invokeFailureCallbacksForIdentifier(_ identifier: String, error: Error) { @@ -249,5 +245,7 @@ class VoiceMessageAttachmentCacheManager { } self.completionCallbacks[callbackKey] = nil + + MXLog.debug("[VoiceMessageAttachmentCacheManager] Failed task with error: \(error)") } } From cb5b591e035c6c9b43d53f85e858cb68cbe4f7fa Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 3 Aug 2021 12:50:03 +0300 Subject: [PATCH 09/13] Voice messages - Increased recording state microphone icon size. --- .../voice_message_record_button_recording.png | Bin 2192 -> 2870 bytes ...ice_message_record_button_recording@2x.png | Bin 4029 -> 5567 bytes ...ice_message_record_button_recording@3x.png | Bin 6082 -> 8299 bytes .../VoiceMessages/VoiceMessageToolbarView.xib | 18 +++++++++--------- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording.png index 8fa147c18efa48fa4b9b3b5be565bce49ecb4305..5972e1272dcf6eae8fdf59c8151d6b1a537fc660 100644 GIT binary patch delta 2848 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 X-Y6uw3G?y*0000<2SrXqu0mjf6F_#l diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording@3x.png index 7fdf91c21d5493754dffcde323f7da899067c2c9..b1def35e1d8b544b9df86620f08aad70c1758917 100644 GIT binary patch literal 8299 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 literal 6082 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 IM6N<$f((78Gynhq diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.xib b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.xib index f11674470..52dcce870 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.xib +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.xib @@ -23,7 +23,7 @@ - + @@ -120,15 +120,15 @@ - + @@ -136,14 +136,14 @@ - + - + @@ -152,7 +152,7 @@ - + @@ -280,7 +280,7 @@ - + From c8454162c19205e6da304d10c40264157406048b Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 3 Aug 2021 14:06:36 +0300 Subject: [PATCH 10/13] Voice messages - Updated recorded audio file name. --- .../VoiceMessageController.swift | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift index f6dc56577..40460f990 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift @@ -29,12 +29,13 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, static let maximumAudioRecordingDuration: TimeInterval = 120.0 static let maximumAudioRecordingLengthReachedThreshold: TimeInterval = 10.0 static let elapsedTimeFormat = "m:ss" + static let fileNameFormat = "'Voice message - 'MM.dd.yyyy HH.mm.ss" static let minimumRecordingDuration = 1.0 } private let themeService: ThemeService private let mediaServiceProvider: VoiceMessageMediaServiceProvider - private let temporaryFileURL: URL + private var temporaryFileURL: URL! private let _voiceMessageToolbarView: VoiceMessageToolbarView private var displayLink: CADisplayLink! @@ -48,12 +49,18 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, private var isInLockedMode: Bool = false private var notifiedRemainingTime = false - private static let timeFormatter: DateFormatter = { + private static let elapsedTimeFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = Constants.elapsedTimeFormat return dateFormatter }() + private static let fileNameDateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = Constants.fileNameFormat + return dateFormatter + }() + @objc public weak var delegate: VoiceMessageControllerDelegate? @objc public var isRecordingAudio: Bool { @@ -68,9 +75,6 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, self.themeService = themeService self.mediaServiceProvider = mediaServiceProvider - let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) - temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(ProcessInfo().globallyUniqueString).appendingPathExtension("m4a") - _voiceMessageToolbarView = VoiceMessageToolbarView.loadFromNib() super.init() @@ -107,6 +111,11 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, audioRecorder = mediaServiceProvider.audioRecorder() audioRecorder?.registerDelegate(self) + + let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + let fileName = VoiceMessageController.fileNameDateFormatter.string(from: Date()) + temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(fileName).appendingPathExtension("m4a") + audioRecorder?.recordWithOutputURL(temporaryFileURL) } @@ -260,7 +269,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, }) dispatchGroup.enter() - let destinationURL = sourceURL.deletingPathExtension().appendingPathExtension("opus") + let destinationURL = sourceURL.deletingPathExtension().appendingPathExtension("ogg") VoiceMessageAudioConverter.convertToOpusOgg(sourceURL: sourceURL, destinationURL: destinationURL) { result in switch result { case .success: @@ -342,7 +351,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, var details = VoiceMessageToolbarViewDetails() details.state = (isRecording ? (isInLockedMode ? .lockedModeRecord : .record) : (isInLockedMode ? .lockedModePlayback : .idle)) - details.elapsedTime = VoiceMessageController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: currentTime)) + details.elapsedTime = VoiceMessageController.elapsedTimeFormatter.string(from: Date(timeIntervalSinceReferenceDate: currentTime)) details.audioSamples = audioSamples if isRecording { @@ -391,7 +400,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, var details = VoiceMessageToolbarViewDetails() details.state = (audioRecorder?.isRecording ?? false ? (isInLockedMode ? .lockedModeRecord : .record) : (isInLockedMode ? .lockedModePlayback : .idle)) - details.elapsedTime = VoiceMessageController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: (audioPlayer.isPlaying ? audioPlayer.currentTime : audioPlayer.duration))) + details.elapsedTime = VoiceMessageController.elapsedTimeFormatter.string(from: Date(timeIntervalSinceReferenceDate: (audioPlayer.isPlaying ? audioPlayer.currentTime : audioPlayer.duration))) details.audioSamples = audioSamples details.isPlaying = audioPlayer.isPlaying details.progress = (audioPlayer.isPlaying ? (audioPlayer.duration > 0.0 ? audioPlayer.currentTime / audioPlayer.duration : 0.0) : 0.0) From d33fa9f4bab1e419d91cc2cb4bfebb7a01b2de50 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 3 Aug 2021 15:12:11 +0300 Subject: [PATCH 11/13] Updated CHANGES.rst --- CHANGES.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c7d5e0920..612d97b69 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,10 +5,11 @@ Changes to be released in next version * 🙌 Improvements - * + * Voice Messages: Increased recording state microphone icon size + * Voice Messages: Using "Voice message - MM.dd.yyyy HH.mm.ss" as the format for recorded audio files 🐛 Bugfix - * + * Voice Messages: Fixed race conditions when sending voice messages (#4641) ⚠️ API Changes * From 6ba1f4d410672118acd8fba52b8fb85c8ee9322d Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 3 Aug 2021 14:44:53 +0200 Subject: [PATCH 12/13] version++ --- CHANGES.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 612d97b69..34b40353d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,4 @@ -Changes to be released in next version +Changes in 1.4.9 (2021-08-03) ================================================= ✨ Features @@ -23,6 +23,9 @@ Changes to be released in next version Others * +Improvements: + + Changes in 1.4.8 (2021-07-29) ================================================= From dc7eefa8b4a709f76b74a7641e04165ade42febc Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 3 Aug 2021 15:16:25 +0200 Subject: [PATCH 13/13] finish version++