Adding progress to audio record player
This commit is contained in:
parent
87074fa61a
commit
49a7e88ee5
@ -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;
|
||||
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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?
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user