Adding progress to audio record player
This commit is contained in:
parent
87074fa61a
commit
49a7e88ee5
@ -1885,7 +1885,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 152;
|
CURRENT_PROJECT_VERSION = 153;
|
||||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
||||||
@ -1912,7 +1912,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 152;
|
CURRENT_PROJECT_VERSION = 153;
|
||||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
||||||
|
|||||||
@ -12,10 +12,12 @@ import AutoCatCore
|
|||||||
struct AudioRecordView: View {
|
struct AudioRecordView: View {
|
||||||
|
|
||||||
let record: AudioRecordViewModel
|
let record: AudioRecordViewModel
|
||||||
|
let progress: Double
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
return HStack {
|
ZStack(alignment: .bottom) {
|
||||||
|
HStack {
|
||||||
playButton
|
playButton
|
||||||
duration
|
duration
|
||||||
Spacer(minLength: 0)
|
Spacer(minLength: 0)
|
||||||
@ -26,6 +28,11 @@ struct AudioRecordView: View {
|
|||||||
.monospacedDigit()
|
.monospacedDigit()
|
||||||
}
|
}
|
||||||
.padding(.trailing)
|
.padding(.trailing)
|
||||||
|
|
||||||
|
if record.isPlaying {
|
||||||
|
ProgressView(value: progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var playButton: some View {
|
var playButton: some View {
|
||||||
@ -64,6 +71,7 @@ struct AudioRecordView: View {
|
|||||||
raw: "бла-бла",
|
raw: "бла-бла",
|
||||||
duration: 145,
|
duration: 145,
|
||||||
event: nil
|
event: nil
|
||||||
), onPlay: {})
|
), onPlay: {}),
|
||||||
|
progress: 0.5
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ struct RecordsScreen: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List(viewModel.recordModels) { record in
|
List(viewModel.recordModels) { record in
|
||||||
AudioRecordView(record: record)
|
AudioRecordView(record: record, progress: viewModel.progress)
|
||||||
.listRowInsets(EdgeInsets())
|
.listRowInsets(EdgeInsets())
|
||||||
.swipeActions(allowsFullSwipe: false) {
|
.swipeActions(allowsFullSwipe: false) {
|
||||||
makeActions(for: record)
|
makeActions(for: record)
|
||||||
|
|||||||
@ -23,6 +23,7 @@ final class RecordsViewModel: ACHudContainer {
|
|||||||
var showRecordingAlert: Bool = false
|
var showRecordingAlert: Bool = false
|
||||||
var records: [AudioRecordDto] = []
|
var records: [AudioRecordDto] = []
|
||||||
var playingRecord: AudioRecordDto?
|
var playingRecord: AudioRecordDto?
|
||||||
|
var progress: Double = 0
|
||||||
|
|
||||||
var recordModels: [AudioRecordViewModel] {
|
var recordModels: [AudioRecordViewModel] {
|
||||||
return records.map { record in
|
return records.map { record in
|
||||||
@ -75,13 +76,21 @@ final class RecordsViewModel: ACHudContainer {
|
|||||||
|
|
||||||
func onPlayTapped(record: AudioRecordDto) {
|
func onPlayTapped(record: AudioRecordDto) {
|
||||||
do {
|
do {
|
||||||
playingRecord = record
|
try recordPlayer.play(record: record, onStop: { [weak self] dto, error in
|
||||||
try recordPlayer.play(record: record) { [weak self] dto, error in
|
|
||||||
self?.playingRecord = nil
|
self?.playingRecord = nil
|
||||||
|
//self?.progress = 0
|
||||||
if let error {
|
if let error {
|
||||||
self?.hud = .error(error)
|
self?.hud = .error(error)
|
||||||
}
|
}
|
||||||
|
}, onProgress: { [weak self] progress in
|
||||||
|
self?.progress = progress
|
||||||
|
print("==== progress: \(progress)")
|
||||||
|
})
|
||||||
|
|
||||||
|
if playingRecord != record {
|
||||||
|
progress = 0
|
||||||
}
|
}
|
||||||
|
playingRecord = record
|
||||||
} catch {
|
} catch {
|
||||||
playingRecord = nil
|
playingRecord = nil
|
||||||
hud = .error(error)
|
hud = .error(error)
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct AudioRecordDto: Decodable, Sendable {
|
public struct AudioRecordDto: Decodable, Sendable, Equatable {
|
||||||
|
|
||||||
public var path: String = ""
|
public var path: String = ""
|
||||||
public var number: String?
|
public var number: String?
|
||||||
|
|||||||
@ -13,8 +13,15 @@ public final class RecordPlayerService: NSObject {
|
|||||||
var player: AVAudioPlayer?
|
var player: AVAudioPlayer?
|
||||||
var record: AudioRecordDto?
|
var record: AudioRecordDto?
|
||||||
var onStop: ((AudioRecordDto, Error?) -> Void)?
|
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)
|
let url = try FileManager.default.url(for: record.path, in: Constants.audioRecordsFolder)
|
||||||
player = try AVAudioPlayer(contentsOf: url)
|
player = try AVAudioPlayer(contentsOf: url)
|
||||||
@ -23,6 +30,9 @@ public final class RecordPlayerService: NSObject {
|
|||||||
self.record = record
|
self.record = record
|
||||||
self.onStop = onStop
|
self.onStop = onStop
|
||||||
|
|
||||||
|
activateProgressTimer()
|
||||||
|
self.onProgress = onProgress
|
||||||
|
|
||||||
try activateSession()
|
try activateSession()
|
||||||
|
|
||||||
player?.play()
|
player?.play()
|
||||||
@ -43,30 +53,48 @@ public final class RecordPlayerService: NSObject {
|
|||||||
options: .notifyOthersOnDeactivation
|
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 {
|
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 {
|
guard let player, self.record?.id == record.id else {
|
||||||
try playNewRecord(record: record, onStop: onStop)
|
try playNewRecord(record: record, onStop: onStop, onProgress: onProgress)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if player.isPlaying {
|
if player.isPlaying {
|
||||||
player.pause()
|
player.pause()
|
||||||
|
deactivateProgressTimer()
|
||||||
try deactivateSession()
|
try deactivateSession()
|
||||||
} else {
|
} else {
|
||||||
try activateSession()
|
try activateSession()
|
||||||
|
activateProgressTimer()
|
||||||
player.play()
|
player.play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func pause() {
|
|
||||||
|
|
||||||
player?.pause()
|
|
||||||
}
|
|
||||||
|
|
||||||
public var currentPlayingId: String? {
|
public var currentPlayingId: String? {
|
||||||
guard player?.isPlaying == true else {
|
guard player?.isPlaying == true else {
|
||||||
return nil
|
return nil
|
||||||
@ -102,6 +130,7 @@ extension RecordPlayerService: AVAudioPlayerDelegate {
|
|||||||
player = nil
|
player = nil
|
||||||
record = nil
|
record = nil
|
||||||
|
|
||||||
|
deactivateProgressTimer()
|
||||||
try? deactivateSession()
|
try? deactivateSession()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,9 @@ public protocol RecordPlayerServiceProtocol {
|
|||||||
|
|
||||||
var currentPlayingId: String? { get }
|
var currentPlayingId: String? { get }
|
||||||
|
|
||||||
func play(record: AudioRecordDto, onStop: ((AudioRecordDto, Error?) -> Void)?) throws
|
func play(
|
||||||
func pause()
|
record: AudioRecordDto,
|
||||||
|
onStop: ((AudioRecordDto, Error?) -> Void)?,
|
||||||
|
onProgress: ((Double) -> Void)?
|
||||||
|
) throws
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,18 +35,23 @@ extension VehicleService: VehicleServiceProtocol {
|
|||||||
number: String,
|
number: String,
|
||||||
forceUpdate: Bool,
|
forceUpdate: Bool,
|
||||||
trackLocation: Bool,
|
trackLocation: Bool,
|
||||||
additionalEvents: [VehicleEventDto],
|
additionalEvent: VehicleEventDto?,
|
||||||
dbUpdatePolicy: DbUpdatePolicy
|
dbUpdatePolicy: DbUpdatePolicy
|
||||||
) async throws -> VehicleWithErrors {
|
) async throws -> VehicleWithErrors {
|
||||||
|
|
||||||
var vehicle = (try? await storageService.loadVehicle(number: number)) ?? VehicleDto(number: number)
|
var vehicle = (try? await storageService.loadVehicle(number: number)) ?? VehicleDto(number: number)
|
||||||
var errors: [Error] = []
|
var errors: [Error] = []
|
||||||
|
|
||||||
// TODO: Check if additionalEvents already was added earlier (avoid duplication)
|
|
||||||
let events = vehicle.events + additionalEvents
|
|
||||||
let notes = vehicle.notes
|
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.events = events
|
||||||
vehicle.updatedDate = Date().timeIntervalSince1970
|
vehicle.updatedDate = Date().timeIntervalSince1970
|
||||||
vehicle.synchronized = false
|
vehicle.synchronized = false
|
||||||
@ -89,7 +94,7 @@ extension VehicleService: VehicleServiceProtocol {
|
|||||||
number: number,
|
number: number,
|
||||||
forceUpdate: false,
|
forceUpdate: false,
|
||||||
trackLocation: false,
|
trackLocation: false,
|
||||||
additionalEvents: [],
|
additionalEvent: nil,
|
||||||
dbUpdatePolicy: .always
|
dbUpdatePolicy: .always
|
||||||
)
|
)
|
||||||
#else
|
#else
|
||||||
@ -97,7 +102,7 @@ extension VehicleService: VehicleServiceProtocol {
|
|||||||
number: number,
|
number: number,
|
||||||
forceUpdate: false,
|
forceUpdate: false,
|
||||||
trackLocation: true,
|
trackLocation: true,
|
||||||
additionalEvents: [],
|
additionalEvent: nil,
|
||||||
dbUpdatePolicy: .always
|
dbUpdatePolicy: .always
|
||||||
)
|
)
|
||||||
#endif
|
#endif
|
||||||
@ -109,7 +114,7 @@ extension VehicleService: VehicleServiceProtocol {
|
|||||||
number: number,
|
number: number,
|
||||||
forceUpdate: true,
|
forceUpdate: true,
|
||||||
trackLocation: false,
|
trackLocation: false,
|
||||||
additionalEvents: [],
|
additionalEvent: nil,
|
||||||
dbUpdatePolicy: .always
|
dbUpdatePolicy: .always
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -120,7 +125,7 @@ extension VehicleService: VehicleServiceProtocol {
|
|||||||
number: number,
|
number: number,
|
||||||
forceUpdate: true,
|
forceUpdate: true,
|
||||||
trackLocation: false,
|
trackLocation: false,
|
||||||
additionalEvents: [],
|
additionalEvent: nil,
|
||||||
dbUpdatePolicy: .ifExists
|
dbUpdatePolicy: .ifExists
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -131,7 +136,7 @@ extension VehicleService: VehicleServiceProtocol {
|
|||||||
number: number,
|
number: number,
|
||||||
forceUpdate: false,
|
forceUpdate: false,
|
||||||
trackLocation: false,
|
trackLocation: false,
|
||||||
additionalEvents: event.flatMap { [$0] } ?? [],
|
additionalEvent: event,
|
||||||
dbUpdatePolicy: .always
|
dbUpdatePolicy: .always
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user