diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageAudioPlayer.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageAudioPlayer.swift index 1fe928302..616ed1d34 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageAudioPlayer.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageAudioPlayer.swift @@ -40,7 +40,7 @@ class VoiceMessageAudioPlayer: NSObject { private var statusObserver: NSKeyValueObservation? private var playbackBufferEmptyObserver: NSKeyValueObservation? private var rateObserver: NSKeyValueObservation? - private var playToEndObsever: NSObjectProtocol? + private var playToEndObserver: NSObjectProtocol? private let delegateContainer = DelegateContainer() @@ -55,23 +55,11 @@ class VoiceMessageAudioPlayer: NSObject { } var duration: TimeInterval { - guard let item = self.audioPlayer?.currentItem else { - return 0 - } - - let duration = CMTimeGetSeconds(item.duration) - - return duration.isNaN ? 0.0 : duration + return abs(CMTimeGetSeconds(self.audioPlayer?.currentItem?.duration ?? .zero)) } var currentTime: TimeInterval { - guard let audioPlayer = self.audioPlayer else { - return 0.0 - } - - let currentTime = CMTimeGetSeconds(audioPlayer.currentTime()) - - return currentTime.isNaN ? 0.0 : currentTime + return abs(CMTimeGetSeconds(audioPlayer?.currentTime() ?? .zero)) } private(set) var isStopped = true @@ -200,7 +188,7 @@ class VoiceMessageAudioPlayer: NSObject { } } - playToEndObsever = NotificationCenter.default.addObserver(forName: Notification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem, queue: nil) { [weak self] notification in + playToEndObserver = NotificationCenter.default.addObserver(forName: Notification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem, queue: nil) { [weak self] notification in guard let self = self else { return } self.delegateContainer.notifyDelegatesWithBlock { delegate in @@ -213,7 +201,7 @@ class VoiceMessageAudioPlayer: NSObject { statusObserver?.invalidate() playbackBufferEmptyObserver?.invalidate() rateObserver?.invalidate() - NotificationCenter.default.removeObserver(playToEndObsever as Any) + NotificationCenter.default.removeObserver(playToEndObserver as Any) } } diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageAudioRecorder.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageAudioRecorder.swift index 0ea7a5594..fafabd79a 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageAudioRecorder.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageAudioRecorder.swift @@ -48,7 +48,7 @@ class VoiceMessageAudioRecorder: NSObject, AVAudioRecorderDelegate { return audioRecorder?.isRecording ?? false } - func recordWithOuputURL(_ url: URL) { + func recordWithOutputURL(_ url: URL) { let settings = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 12000, diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift index ea78372ff..f6dc56577 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift @@ -28,6 +28,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, private enum Constants { static let maximumAudioRecordingDuration: TimeInterval = 120.0 static let maximumAudioRecordingLengthReachedThreshold: TimeInterval = 10.0 + static let elapsedTimeFormat = "m:ss" static let minimumRecordingDuration = 1.0 } @@ -47,6 +48,12 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, private var isInLockedMode: Bool = false private var notifiedRemainingTime = false + private static let timeFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = Constants.elapsedTimeFormat + return dateFormatter + }() + @objc public weak var delegate: VoiceMessageControllerDelegate? @objc public var isRecordingAudio: Bool { @@ -90,7 +97,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, // Haptic are not played during record on iOS by default. This fix works // only since iOS 13. A workaround for iOS 12 and earlier would be to - // dispatch after at least 100ms recordWithOuputURL call + // dispatch after at least 100ms recordWithOutputURL call if #available(iOS 13.0, *) { try? AVAudioSession.sharedInstance().setCategory(.playAndRecord) try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true) @@ -100,7 +107,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, audioRecorder = mediaServiceProvider.audioRecorder() audioRecorder?.registerDelegate(self) - audioRecorder?.recordWithOuputURL(temporaryFileURL) + audioRecorder?.recordWithOutputURL(temporaryFileURL) } func voiceMessageToolbarViewDidRequestRecordingFinish(_ toolbarView: VoiceMessageToolbarView) { @@ -335,7 +342,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, var details = VoiceMessageToolbarViewDetails() details.state = (isRecording ? (isInLockedMode ? .lockedModeRecord : .record) : (isInLockedMode ? .lockedModePlayback : .idle)) - details.elapsedTime = durationStringFromTimeInterval(currentTime) + details.elapsedTime = VoiceMessageController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: currentTime)) details.audioSamples = audioSamples if isRecording { @@ -384,7 +391,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, var details = VoiceMessageToolbarViewDetails() details.state = (audioRecorder?.isRecording ?? false ? (isInLockedMode ? .lockedModeRecord : .record) : (isInLockedMode ? .lockedModePlayback : .idle)) - details.elapsedTime = durationStringFromTimeInterval(audioPlayer.isPlaying ? audioPlayer.currentTime : audioPlayer.duration) + details.elapsedTime = VoiceMessageController.timeFormatter.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) @@ -399,18 +406,4 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, audioSamples = audioSamples + [Float](repeating: 0.0, count: delta) } - - private func durationStringFromTimeInterval(_ interval: TimeInterval) -> String { - guard interval.isFinite else { - return "" - } - - var timeInterval = abs(interval) - let hours = trunc(timeInterval / 3600.0) - timeInterval -= hours * 3600.0 - let minutes = trunc(timeInterval / 60.0) - timeInterval -= minutes * 60.0 - - return String(format: "%01.0f:%02.0f", minutes, timeInterval) - } } diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageMediaServiceProvider.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageMediaServiceProvider.swift index 82a16c977..7def8d7e1 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageMediaServiceProvider.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageMediaServiceProvider.swift @@ -75,12 +75,12 @@ import Foundation // MARK: - Private private func stopAllServicesExcept(_ service: AnyObject?) { - for audioRecoder in audioRecorders.allObjects { - if audioRecoder === service { + for audioRecorder in audioRecorders.allObjects { + if audioRecorder === service { continue } - audioRecoder.stopRecording() + audioRecorder.stopRecording() } guard let audioPlayersEnumerator = audioPlayers.objectEnumerator() else { diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackController.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackController.swift index 9abc99e3b..8fcbc55e4 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackController.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackController.swift @@ -26,6 +26,10 @@ enum VoiceMessagePlaybackControllerState { class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMessagePlaybackViewDelegate { + private enum Constants { + static let elapsedTimeFormat = "m:ss" + } + private let mediaServiceProvider: VoiceMessageMediaServiceProvider private let cacheManager: VoiceMessageAttachmentCacheManager @@ -43,6 +47,13 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess } } + private static let timeFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = Constants.elapsedTimeFormat + return dateFormatter + }() + + let playbackView: VoiceMessagePlaybackView init(mediaServiceProvider: VoiceMessageMediaServiceProvider, @@ -134,11 +145,11 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess switch state { case .stopped: - details.currentTime = durationStringFromTimeInterval(self.duration) + details.currentTime = VoiceMessagePlaybackController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: self.duration)) details.progress = 0.0 default: if let audioPlayer = audioPlayer { - details.currentTime = durationStringFromTimeInterval(audioPlayer.currentTime) + details.currentTime = VoiceMessagePlaybackController.timeFormatter.string(from: Date(timeIntervalSinceReferenceDate: audioPlayer.currentTime)) details.progress = (audioPlayer.duration > 0.0 ? audioPlayer.currentTime / audioPlayer.duration : 0.0) } } @@ -199,18 +210,4 @@ class VoiceMessagePlaybackController: VoiceMessageAudioPlayerDelegate, VoiceMess @objc private func updateTheme() { playbackView.update(theme: ThemeService.shared().theme) } - - private func durationStringFromTimeInterval(_ interval: TimeInterval) -> String { - guard interval.isFinite else { - return "" - } - - var timeInterval = abs(interval) - let hours = trunc(timeInterval / 3600.0) - timeInterval -= hours * 3600.0 - let minutes = trunc(timeInterval / 60.0) - timeInterval -= minutes * 60.0 - - return String(format: "%01.0f:%02.0f", minutes, timeInterval) - } } diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift index e3f53a434..443168e2f 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift @@ -115,7 +115,7 @@ class VoiceMessagePlaybackView: UIView, NibLoadable, Themable { playButton.backgroundColor = theme.colors.background playButton.tintColor = theme.colors.secondaryContent backgroundView.backgroundColor = theme.colors.quinaryContent - _waveformView.primarylineColor = theme.colors.quarterlyContent + _waveformView.primaryLineColor = theme.colors.quarterlyContent _waveformView.secondaryLineColor = theme.colors.secondaryContent elapsedTimeLabel.textColor = theme.colors.tertiaryContent } diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift index 3cfaebff7..4650d6fae 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift @@ -200,7 +200,7 @@ class VoiceMessageToolbarView: PassthroughView, NibLoadable, Themable, UIGesture case UIGestureRecognizer.State.began: delegate?.voiceMessageToolbarViewDidRequestRecordingStart(self) case UIGestureRecognizer.State.ended: - delegate?.voiceMessageToolbarViewDidRequestRecordingFinish(self) + delegate?.voiceMessageToolbarViewDidRequestRecordingFinish(self) default: break } diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageWaveformView.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageWaveformView.swift index 9a72c0902..8685109c4 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageWaveformView.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageWaveformView.swift @@ -28,10 +28,10 @@ class VoiceMessageWaveformView: UIView { } } - var primarylineColor = UIColor.lightGray { + var primaryLineColor = UIColor.lightGray { didSet { - backgroundLayer.strokeColor = primarylineColor.cgColor - backgroundLayer.fillColor = primarylineColor.cgColor + backgroundLayer.strokeColor = primaryLineColor.cgColor + backgroundLayer.fillColor = primaryLineColor.cgColor } } var secondaryLineColor = UIColor.darkGray { @@ -60,7 +60,7 @@ class VoiceMessageWaveformView: UIView { override init(frame: CGRect) { super.init(frame: frame) - setupAndAdd(backgroundLayer, with: primarylineColor) + setupAndAdd(backgroundLayer, with: primaryLineColor) setupAndAdd(progressLayer, with: secondaryLineColor) progressLayer.masksToBounds = true diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 92cfd7e61..11bbcb3d8 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -2802,7 +2802,7 @@ TableViewSectionsDelegate> } } -- (void)togglePushNotifications:(id)sender +- (void)togglePushNotifications:(UISwitch *)sender { // Check first whether the user allow notification from device settings UIUserNotificationType currentUserNotificationTypes = UIApplication.sharedApplication.currentUserNotificationSettings.types; @@ -2832,7 +2832,7 @@ TableViewSectionsDelegate> [self presentViewController:currentAlert animated:YES completion:nil]; // Keep off the switch - ((UISwitch*)sender).on = NO; + sender.on = NO; } else if ([MXKAccountManager sharedManager].activeAccounts.count) { @@ -2855,7 +2855,7 @@ TableViewSectionsDelegate> [[AppDelegate theDelegate] registerForRemoteNotificationsWithCompletion:^(NSError * error) { if (error) { - [(UISwitch *)sender setOn:NO animated:YES]; + [sender setOn:NO animated:YES]; [self stopActivityIndicator]; } else @@ -2871,49 +2871,42 @@ TableViewSectionsDelegate> } } -- (void)toggleCallKit:(id)sender +- (void)toggleCallKit:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - [MXKAppSettings standardAppSettings].enableCallKit = switchButton.isOn; + [MXKAppSettings standardAppSettings].enableCallKit = sender.isOn; } -- (void)toggleStunServerFallback:(id)sender +- (void)toggleStunServerFallback:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - RiotSettings.shared.allowStunServerFallback = switchButton.isOn; + RiotSettings.shared.allowStunServerFallback = sender.isOn; self.mainSession.callManager.fallbackSTUNServer = RiotSettings.shared.allowStunServerFallback ? BuildSettings.stunServerFallbackUrlString : nil; } -- (void)toggleAllowIntegrations:(id)sender +- (void)toggleAllowIntegrations:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - MXSession *session = self.mainSession; [self startActivityIndicator]; - + __block RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session]; - [sharedSettings setIntegrationProvisioningWithEnabled:switchButton.on success:^{ + [sharedSettings setIntegrationProvisioningWithEnabled:sender.isOn success:^{ sharedSettings = nil; [self stopActivityIndicator]; } failure:^(NSError * _Nullable error) { sharedSettings = nil; - [switchButton setOn:!switchButton.on animated:YES]; + [sender setOn:!sender.isOn animated:YES]; [self stopActivityIndicator]; }]; } -- (void)toggleShowDecodedContent:(id)sender +- (void)toggleShowDecodedContent:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - RiotSettings.shared.showDecryptedContentInNotifications = switchButton.isOn; + RiotSettings.shared.showDecryptedContentInNotifications = sender.isOn; } -- (void)toggleLocalContactsSync:(id)sender +- (void)toggleLocalContactsSync:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - - if (switchButton.on) + if (sender.on) { [MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController:self completionHandler:^(BOOL granted) { @@ -2954,57 +2947,36 @@ TableViewSectionsDelegate> } } -- (void)toggleEnableRageShake:(id)sender +- (void)toggleEnableRageShake:(UISwitch *)sender { - if (sender && [sender isKindOfClass:UISwitch.class]) - { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.enableRageShake = switchButton.isOn; - - [self updateSections]; - } + RiotSettings.shared.enableRageShake = sender.isOn; + + [self updateSections]; } - (void)toggleEnableRingingForGroupCalls:(UISwitch *)sender { - if (sender) - { - RiotSettings.shared.enableRingingForGroupCalls = sender.isOn; - - [self.tableView reloadData]; - } + RiotSettings.shared.enableRingingForGroupCalls = sender.isOn; } - (void)toggleEnableVoiceMessages:(UISwitch *)sender { - if (sender) - { - RiotSettings.shared.enableVoiceMessages = sender.isOn; - - [self.tableView reloadData]; - } + RiotSettings.shared.enableVoiceMessages = sender.isOn; } -- (void)togglePinRoomsWithMissedNotif:(id)sender +- (void)togglePinRoomsWithMissedNotif:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome = switchButton.on; + RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome = sender.isOn; } -- (void)togglePinRoomsWithUnread:(id)sender +- (void)togglePinRoomsWithUnread:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome = switchButton.on; + RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome = sender.on; } -- (void)toggleCommunityFlair:(id)sender +- (void)toggleCommunityFlair:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:switchButton.tag inSection:groupsDataSource.joinedGroupsSection]; + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:groupsDataSource.joinedGroupsSection]; id groupCellData = [groupsDataSource cellDataAtIndex:indexPath]; MXGroup *group = groupCellData.group; @@ -3014,7 +2986,7 @@ TableViewSectionsDelegate> __weak typeof(self) weakSelf = self; - [self.mainSession updateGroupPublicity:group isPublicised:switchButton.on success:^{ + [self.mainSession updateGroupPublicity:group isPublicised:sender.isOn success:^{ if (weakSelf) { @@ -3030,7 +3002,7 @@ TableViewSectionsDelegate> [self stopActivityIndicator]; // Come back to previous state button - [switchButton setOn:!switchButton.isOn animated:YES]; + [sender setOn:!sender.isOn animated:YES]; // Notify user [[AppDelegate theDelegate] showErrorAsAlert:error]; @@ -3676,16 +3648,9 @@ TableViewSectionsDelegate> animated:YES]; } -- (void)toggleNSFWPublicRoomsFiltering:(id)sender +- (void)toggleNSFWPublicRoomsFiltering:(UISwitch *)sender { - if (sender && [sender isKindOfClass:UISwitch.class]) - { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.showNSFWPublicRooms = switchButton.isOn; - - [self.tableView reloadData]; - } + RiotSettings.shared.showNSFWPublicRooms = sender.isOn; } #pragma mark - TextField listener