Adding progress to audio record player

This commit is contained in:
Selim Mustafaev 2025-04-03 22:55:09 +03:00
parent 87074fa61a
commit 49a7e88ee5
8 changed files with 90 additions and 36 deletions

View File

@ -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;

View File

@ -12,10 +12,12 @@ import AutoCatCore
struct AudioRecordView: View {
let record: AudioRecordViewModel
let progress: Double
var body: some View {
return HStack {
ZStack(alignment: .bottom) {
HStack {
playButton
duration
Spacer(minLength: 0)
@ -26,6 +28,11 @@ struct AudioRecordView: View {
.monospacedDigit()
}
.padding(.trailing)
if record.isPlaying {
ProgressView(value: progress)
}
}
}
var playButton: some View {
@ -64,6 +71,7 @@ struct AudioRecordView: View {
raw: "бла-бла",
duration: 145,
event: nil
), onPlay: {})
), onPlay: {}),
progress: 0.5
)
}

View File

@ -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)

View File

@ -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)

View File

@ -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?

View File

@ -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()
}
}

View File

@ -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
}

View File

@ -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
)
}