diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 4715d2ac3..d4c6ff729 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -572,6 +572,7 @@ C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */; }; C31D1DE9252172D4005D4DA8 /* ContactUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */; }; C31F8117252546F200DD9FD9 /* file_example_MP3_2MG.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C31F8116252546F200DD9FD9 /* file_example_MP3_2MG.mp3 */; }; + C31F812625258FB000DD9FD9 /* Storage+VolumeSamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */; }; C329FEEC24F7277900B1C64C /* LightModeSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEEB24F7277900B1C64C /* LightModeSheet.swift */; }; C329FEEF24F7743F00B1C64C /* UIViewController+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */; }; C34C8F7423A7830B00D82669 /* SpaceMono-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */; }; @@ -1372,6 +1373,7 @@ C31D1DE22521718E005D4DA8 /* UserSelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSelectionVC.swift; sourceTree = ""; }; C31D1DE8252172D4005D4DA8 /* ContactUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactUtilities.swift; sourceTree = ""; }; C31F8116252546F200DD9FD9 /* file_example_MP3_2MG.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = file_example_MP3_2MG.mp3; sourceTree = ""; }; + C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Storage+VolumeSamples.swift"; sourceTree = ""; }; C329FEEB24F7277900B1C64C /* LightModeSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightModeSheet.swift; sourceTree = ""; }; C329FEED24F7742E00B1C64C /* UIViewController+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Utilities.swift"; sourceTree = ""; }; C34C8F7323A7830A00D82669 /* SpaceMono-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SpaceMono-Bold.ttf"; sourceTree = ""; }; @@ -2602,6 +2604,7 @@ isa = PBXGroup; children = ( B8CCF63B239757C10091D419 /* Components */, + C31F812425258F9C00DD9FD9 /* Database */, C32B405424A961E1001117B5 /* Dependencies */, B8CCF63C239757DB0091D419 /* Utilities */, B8CCF63D2397580E0091D419 /* View Controllers */, @@ -2754,6 +2757,14 @@ path = "View Controllers"; sourceTree = ""; }; + C31F812425258F9C00DD9FD9 /* Database */ = { + isa = PBXGroup; + children = ( + C31F812525258FB000DD9FD9 /* Storage+VolumeSamples.swift */, + ); + path = Database; + sourceTree = ""; + }; C32B405424A961E1001117B5 /* Dependencies */ = { isa = PBXGroup; children = ( @@ -3998,6 +4009,7 @@ 340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */, B88847BC23E10BC6009836D2 /* GroupMembersVC.swift in Sources */, B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */, + C31F812625258FB000DD9FD9 /* Storage+VolumeSamples.swift in Sources */, B85357C723A1FB5100AAF6CD /* LinkDeviceVCDelegate.swift in Sources */, 340FC8C5204DE223007AEB0F /* DebugUIBackup.m in Sources */, C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */, diff --git a/Signal/src/Loki/Components/VoiceMessageView2.swift b/Signal/src/Loki/Components/VoiceMessageView2.swift index 257688225..4b614dac0 100644 --- a/Signal/src/Loki/Components/VoiceMessageView2.swift +++ b/Signal/src/Loki/Components/VoiceMessageView2.swift @@ -6,7 +6,6 @@ final class VoiceMessageView2 : UIView { private var isAnimating = false private var volumeSamples: [Float] = [] { didSet { updateShapeLayers() } } private var progress: CGFloat = 0 - private var duration: CGFloat = 1 // Not initialized at 0 to avoid division by zero // MARK: Components private lazy var loader: UIView = { @@ -31,6 +30,8 @@ final class VoiceMessageView2 : UIView { private let margin: CGFloat = 4 private let sampleSpacing: CGFloat = 1 + @objc public static let contentHeight: CGFloat = 40 + // MARK: Initialization @objc(initWithVoiceMessage:) init(voiceMessage: TSAttachment) { @@ -54,12 +55,21 @@ final class VoiceMessageView2 : UIView { guard let url = (voiceMessage as? TSAttachmentStream)?.originalMediaURL else { return print("[Loki] Couldn't get URL for voice message.") } - AudioUtilities.getVolumeSamples(for: url).done(on: DispatchQueue.main) { [weak self] volumeSamples in - guard let self = self else { return } - self.volumeSamples = volumeSamples + if let cachedVolumeSamples = Storage.getVolumeSamples(for: voiceMessage.uniqueId!) { + self.volumeSamples = cachedVolumeSamples self.stopAnimating() - }.catch(on: DispatchQueue.main) { error in - print("[Loki] Couldn't sample audio file due to error: \(error).") + } else { + let voiceMessageID = voiceMessage.uniqueId! + AudioUtilities.getVolumeSamples(for: url).done(on: DispatchQueue.main) { [weak self] volumeSamples in + guard let self = self else { return } + self.volumeSamples = volumeSamples + Storage.write { transaction in + Storage.setVolumeSamples(for: voiceMessageID, to: volumeSamples, using: transaction) + } + self.stopAnimating() + }.catch(on: DispatchQueue.main) { error in + print("[Loki] Couldn't sample audio file due to error: \(error).") + } } } else { showLoader() @@ -68,7 +78,7 @@ final class VoiceMessageView2 : UIView { private func setUpViewHierarchy() { set(.width, to: 200) - set(.height, to: 40) + set(.height, to: VoiceMessageView2.contentHeight) addSubview(loader) loader.pin(to: self) layer.insertSublayer(backgroundShapeLayer, at: 0) @@ -83,9 +93,9 @@ final class VoiceMessageView2 : UIView { } private func animateLoader() { - loader.frame = CGRect(x: 0, y: 0, width: 0, height: 40) + loader.frame = CGRect(x: 0, y: 0, width: 0, height: VoiceMessageView2.contentHeight) UIView.animate(withDuration: 2) { [weak self] in - self?.loader.frame = CGRect(x: 0, y: 0, width: 200, height: 40) + self?.loader.frame = CGRect(x: 0, y: 0, width: 200, height: VoiceMessageView2.contentHeight) } completion: { [weak self] _ in guard let self = self else { return } if self.isAnimating { self.animateLoader() } @@ -102,10 +112,9 @@ final class VoiceMessageView2 : UIView { updateShapeLayers() } - @objc(updateForProgress:duration:) - func update(for progress: CGFloat, duration: CGFloat) { + @objc(updateForProgress:) + func update(for progress: CGFloat) { self.progress = progress - self.duration = duration updateShapeLayers() } @@ -125,7 +134,7 @@ final class VoiceMessageView2 : UIView { let y = margin + (h - sH) / 2 let subPath = UIBezierPath(roundedRect: CGRect(x: x, y: y, width: sW, height: sH), cornerRadius: sW / 2) backgroundPath.append(subPath) - if progress / duration > CGFloat(i) / CGFloat(volumeSamples.count) { foregroundPath.append(subPath) } + if progress > CGFloat(i) / CGFloat(volumeSamples.count) { foregroundPath.append(subPath) } } backgroundPath.close() foregroundPath.close() diff --git a/Signal/src/Loki/Database/Storage+VolumeSamples.swift b/Signal/src/Loki/Database/Storage+VolumeSamples.swift new file mode 100644 index 000000000..dce064b7e --- /dev/null +++ b/Signal/src/Loki/Database/Storage+VolumeSamples.swift @@ -0,0 +1,17 @@ + +extension Storage { + + static let volumeSamplesCollection = "LokiVolumeSamplesCollection" + + static func getVolumeSamples(for attachment: String) -> [Float]? { + var result: [Float]? + read { transaction in + result = transaction.object(forKey: attachment, inCollection: volumeSamplesCollection) as? [Float] + } + return result + } + + static func setVolumeSamples(for attachment: String, to volumeSamples: [Float], using transaction: YapDatabaseReadWriteTransaction) { + transaction.setObject(volumeSamples, forKey: attachment, inCollection: volumeSamplesCollection) + } +} diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m index 1805582e0..d75bf7327 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageBubbleView.m @@ -1063,7 +1063,7 @@ NS_ASSUME_NONNULL_BEGIN return nil; } case OWSMessageCellType_Audio: - result = CGSizeMake(maxMessageWidth, 40.0f); + result = CGSizeMake(maxMessageWidth, LKVoiceMessageView2.contentHeight); break; case OWSMessageCellType_GenericAttachment: { TSAttachment *attachment = (self.viewItem.attachmentStream ?: self.viewItem.attachmentPointer); diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index 107de2324..986f9b618 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -484,7 +484,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) self.audioProgressSeconds = progress; - [self.lastAudioMessageView updateForProgress:progress duration:duration]; + [self.lastAudioMessageView updateForProgress:progress / duration]; } #pragma mark - Displayable Text diff --git a/SignalMessaging/utils/OWSAudioPlayer.m b/SignalMessaging/utils/OWSAudioPlayer.m index 2d55dce60..3c099bea5 100644 --- a/SignalMessaging/utils/OWSAudioPlayer.m +++ b/SignalMessaging/utils/OWSAudioPlayer.m @@ -146,7 +146,7 @@ NS_ASSUME_NONNULL_BEGIN [self.audioPlayer play]; [self.audioPlayerPoller invalidate]; - self.audioPlayerPoller = [NSTimer weakScheduledTimerWithTimeInterval:.5f + self.audioPlayerPoller = [NSTimer weakScheduledTimerWithTimeInterval:.05f target:self selector:@selector(audioPlayerUpdated:) userInfo:nil