diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 4b330c3..7b3e3c6 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -1885,7 +1885,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 152; + CURRENT_PROJECT_VERSION = 153; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AutoCat; @@ -1912,7 +1912,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 152; + CURRENT_PROJECT_VERSION = 153; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AutoCat; diff --git a/AutoCat/Screens/RecordsScreen/AudioRecordView/AudioRecordView.swift b/AutoCat/Screens/RecordsScreen/AudioRecordView/AudioRecordView.swift index c5ceca6..d5cdcb3 100644 --- a/AutoCat/Screens/RecordsScreen/AudioRecordView/AudioRecordView.swift +++ b/AutoCat/Screens/RecordsScreen/AudioRecordView/AudioRecordView.swift @@ -12,20 +12,27 @@ import AutoCatCore struct AudioRecordView: View { let record: AudioRecordViewModel + let progress: Double var body: some View { - return HStack { - playButton - duration - Spacer(minLength: 0) - number - Spacer(minLength: 0) - Text(record.date) - .font(.subheadline) - .monospacedDigit() + ZStack(alignment: .bottom) { + HStack { + playButton + duration + Spacer(minLength: 0) + number + Spacer(minLength: 0) + Text(record.date) + .font(.subheadline) + .monospacedDigit() + } + .padding(.trailing) + + if record.isPlaying { + ProgressView(value: progress) + } } - .padding(.trailing) } var playButton: some View { @@ -64,6 +71,7 @@ struct AudioRecordView: View { raw: "бла-бла", duration: 145, event: nil - ), onPlay: {}) + ), onPlay: {}), + progress: 0.5 ) } diff --git a/AutoCat/Screens/RecordsScreen/RecordsScreen.swift b/AutoCat/Screens/RecordsScreen/RecordsScreen.swift index 99effe8..919439f 100644 --- a/AutoCat/Screens/RecordsScreen/RecordsScreen.swift +++ b/AutoCat/Screens/RecordsScreen/RecordsScreen.swift @@ -19,7 +19,7 @@ struct RecordsScreen: View { var body: some View { List(viewModel.recordModels) { record in - AudioRecordView(record: record) + AudioRecordView(record: record, progress: viewModel.progress) .listRowInsets(EdgeInsets()) .swipeActions(allowsFullSwipe: false) { makeActions(for: record) diff --git a/AutoCat/Screens/RecordsScreen/RecordsViewModel.swift b/AutoCat/Screens/RecordsScreen/RecordsViewModel.swift index 8aa08e6..be2f501 100644 --- a/AutoCat/Screens/RecordsScreen/RecordsViewModel.swift +++ b/AutoCat/Screens/RecordsScreen/RecordsViewModel.swift @@ -23,6 +23,7 @@ final class RecordsViewModel: ACHudContainer { var showRecordingAlert: Bool = false var records: [AudioRecordDto] = [] var playingRecord: AudioRecordDto? + var progress: Double = 0 var recordModels: [AudioRecordViewModel] { return records.map { record in @@ -75,13 +76,21 @@ final class RecordsViewModel: ACHudContainer { func onPlayTapped(record: AudioRecordDto) { do { - playingRecord = record - try recordPlayer.play(record: record) { [weak self] dto, error in + try recordPlayer.play(record: record, onStop: { [weak self] dto, error in self?.playingRecord = nil + //self?.progress = 0 if let error { self?.hud = .error(error) } + }, onProgress: { [weak self] progress in + self?.progress = progress + print("==== progress: \(progress)") + }) + + if playingRecord != record { + progress = 0 } + playingRecord = record } catch { playingRecord = nil hud = .error(error) diff --git a/AutoCatCore/Models/DTO/AudioRecordDto.swift b/AutoCatCore/Models/DTO/AudioRecordDto.swift index b610af4..d9aa521 100644 --- a/AutoCatCore/Models/DTO/AudioRecordDto.swift +++ b/AutoCatCore/Models/DTO/AudioRecordDto.swift @@ -8,7 +8,7 @@ import Foundation -public struct AudioRecordDto: Decodable, Sendable { +public struct AudioRecordDto: Decodable, Sendable, Equatable { public var path: String = "" public var number: String? diff --git a/AutoCatCore/Services/RecordPlayerService/RecordPlayerService.swift b/AutoCatCore/Services/RecordPlayerService/RecordPlayerService.swift index ec32978..0271f01 100644 --- a/AutoCatCore/Services/RecordPlayerService/RecordPlayerService.swift +++ b/AutoCatCore/Services/RecordPlayerService/RecordPlayerService.swift @@ -13,8 +13,15 @@ public final class RecordPlayerService: NSObject { var player: AVAudioPlayer? var record: AudioRecordDto? var onStop: ((AudioRecordDto, Error?) -> Void)? + var onProgress: ((Double) -> Void)? - func playNewRecord(record: AudioRecordDto, onStop: ((AudioRecordDto, Error?) -> Void)?) throws { + var progressTimer: Timer? + + func playNewRecord( + record: AudioRecordDto, + onStop: ((AudioRecordDto, Error?) -> Void)?, + onProgress: ((Double) -> Void)? + ) throws { let url = try FileManager.default.url(for: record.path, in: Constants.audioRecordsFolder) player = try AVAudioPlayer(contentsOf: url) @@ -23,6 +30,9 @@ public final class RecordPlayerService: NSObject { self.record = record self.onStop = onStop + activateProgressTimer() + self.onProgress = onProgress + try activateSession() player?.play() @@ -43,30 +53,48 @@ public final class RecordPlayerService: NSObject { options: .notifyOthersOnDeactivation ) } + + func activateProgressTimer() { + + progressTimer?.invalidate() + progressTimer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { [weak self] _ in + if let player = self?.player { + let progress = player.currentTime/player.duration + self?.onProgress?(progress) + } + } + } + + func deactivateProgressTimer() { + + progressTimer?.invalidate() + progressTimer = nil + } } extension RecordPlayerService: RecordPlayerServiceProtocol { - public func play(record: AudioRecordDto, onStop: ((AudioRecordDto, Error?) -> Void)?) throws { + public func play( + record: AudioRecordDto, + onStop: ((AudioRecordDto, Error?) -> Void)?, + onProgress: ((Double) -> Void)? + ) throws { guard let player, self.record?.id == record.id else { - try playNewRecord(record: record, onStop: onStop) + try playNewRecord(record: record, onStop: onStop, onProgress: onProgress) return } if player.isPlaying { player.pause() + deactivateProgressTimer() try deactivateSession() } else { try activateSession() + activateProgressTimer() player.play() } } - public func pause() { - - player?.pause() - } - public var currentPlayingId: String? { guard player?.isPlaying == true else { return nil @@ -102,6 +130,7 @@ extension RecordPlayerService: AVAudioPlayerDelegate { player = nil record = nil + deactivateProgressTimer() try? deactivateSession() } } diff --git a/AutoCatCore/Services/RecordPlayerService/RecordPlayerServiceProtocol.swift b/AutoCatCore/Services/RecordPlayerService/RecordPlayerServiceProtocol.swift index 3c2ca5f..62dbf12 100644 --- a/AutoCatCore/Services/RecordPlayerService/RecordPlayerServiceProtocol.swift +++ b/AutoCatCore/Services/RecordPlayerService/RecordPlayerServiceProtocol.swift @@ -12,6 +12,9 @@ public protocol RecordPlayerServiceProtocol { var currentPlayingId: String? { get } - func play(record: AudioRecordDto, onStop: ((AudioRecordDto, Error?) -> Void)?) throws - func pause() + func play( + record: AudioRecordDto, + onStop: ((AudioRecordDto, Error?) -> Void)?, + onProgress: ((Double) -> Void)? + ) throws } diff --git a/AutoCatCore/Services/VehicleService/VehicleService.swift b/AutoCatCore/Services/VehicleService/VehicleService.swift index 789379b..0dac9f0 100644 --- a/AutoCatCore/Services/VehicleService/VehicleService.swift +++ b/AutoCatCore/Services/VehicleService/VehicleService.swift @@ -35,18 +35,23 @@ extension VehicleService: VehicleServiceProtocol { number: String, forceUpdate: Bool, trackLocation: Bool, - additionalEvents: [VehicleEventDto], + additionalEvent: VehicleEventDto?, dbUpdatePolicy: DbUpdatePolicy ) async throws -> VehicleWithErrors { var vehicle = (try? await storageService.loadVehicle(number: number)) ?? VehicleDto(number: number) var errors: [Error] = [] - // TODO: Check if additionalEvents already was added earlier (avoid duplication) - let events = vehicle.events + additionalEvents let notes = vehicle.notes - if !additionalEvents.isEmpty { + let events: [VehicleEventDto] + if let additionalEvent, !vehicle.events.contains(where: { $0.id == additionalEvent.id }) { + events = vehicle.events + [additionalEvent] + } else { + events = vehicle.events + } + + if additionalEvent != nil { vehicle.events = events vehicle.updatedDate = Date().timeIntervalSince1970 vehicle.synchronized = false @@ -89,7 +94,7 @@ extension VehicleService: VehicleServiceProtocol { number: number, forceUpdate: false, trackLocation: false, - additionalEvents: [], + additionalEvent: nil, dbUpdatePolicy: .always ) #else @@ -97,7 +102,7 @@ extension VehicleService: VehicleServiceProtocol { number: number, forceUpdate: false, trackLocation: true, - additionalEvents: [], + additionalEvent: nil, dbUpdatePolicy: .always ) #endif @@ -109,7 +114,7 @@ extension VehicleService: VehicleServiceProtocol { number: number, forceUpdate: true, trackLocation: false, - additionalEvents: [], + additionalEvent: nil, dbUpdatePolicy: .always ) } @@ -120,7 +125,7 @@ extension VehicleService: VehicleServiceProtocol { number: number, forceUpdate: true, trackLocation: false, - additionalEvents: [], + additionalEvent: nil, dbUpdatePolicy: .ifExists ) } @@ -131,7 +136,7 @@ extension VehicleService: VehicleServiceProtocol { number: number, forceUpdate: false, trackLocation: false, - additionalEvents: event.flatMap { [$0] } ?? [], + additionalEvent: event, dbUpdatePolicy: .always ) }