diff --git a/Werkout_ios.xcodeproj/project.pbxproj b/Werkout_ios.xcodeproj/project.pbxproj index 024bde7..d6a9861 100644 --- a/Werkout_ios.xcodeproj/project.pbxproj +++ b/Werkout_ios.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 1C4AFF1B2A65FB190027710B /* CurrentWorkoutInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */; }; 1C4AFF1C2A65FB2B0027710B /* CurrentWorkoutInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */; }; 1C4AFF1E2A7579410027710B /* NSFWVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF1D2A7579410027710B /* NSFWVideo.swift */; }; + 1C4AFF202A8800860027710B /* AudioQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF1F2A8800860027710B /* AudioQueue.swift */; }; + 1C4AFF212A8801090027710B /* AudioQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C4AFF1F2A8800860027710B /* AudioQueue.swift */; }; 1C5190C22A57CA5F00885849 /* OvalTextFieldStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C12A57CA5F00885849 /* OvalTextFieldStyle.swift */; }; 1C5190C42A589CAC00885849 /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C32A589CAC00885849 /* InfoView.swift */; }; 1C5190C62A589CC100885849 /* ActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5190C52A589CC100885849 /* ActionsView.swift */; }; @@ -137,6 +139,7 @@ 1C4AFF172A65CD290027710B /* Superset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Superset.swift; sourceTree = ""; }; 1C4AFF1A2A65FB190027710B /* CurrentWorkoutInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentWorkoutInfo.swift; sourceTree = ""; }; 1C4AFF1D2A7579410027710B /* NSFWVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSFWVideo.swift; sourceTree = ""; }; + 1C4AFF1F2A8800860027710B /* AudioQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioQueue.swift; sourceTree = ""; }; 1C5190C12A57CA5F00885849 /* OvalTextFieldStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OvalTextFieldStyle.swift; sourceTree = ""; }; 1C5190C32A589CAC00885849 /* InfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = ""; }; 1C5190C52A589CC100885849 /* ActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionsView.swift; sourceTree = ""; }; @@ -325,6 +328,7 @@ 1CF65A422A39FB410042FFBD /* Workout.swift */, 1C4AFF172A65CD290027710B /* Superset.swift */, 1C4AFF1D2A7579410027710B /* NSFWVideo.swift */, + 1C4AFF1F2A8800860027710B /* AudioQueue.swift */, ); path = APIModels; sourceTree = ""; @@ -602,6 +606,7 @@ 1C4AFF1E2A7579410027710B /* NSFWVideo.swift in Sources */, 1C5190D02A589D5F00885849 /* AllWorkoutsListView.swift in Sources */, 1C5190CA2A589CEC00885849 /* ExerciseListView.swift in Sources */, + 1C4AFF202A8800860027710B /* AudioQueue.swift in Sources */, 1CAF4D8A2A5132F900B00E50 /* PlannedWorkout.swift in Sources */, 1C5190C22A57CA5F00885849 /* OvalTextFieldStyle.swift in Sources */, 1C5190CC2A589D0000885849 /* CountdownView.swift in Sources */, @@ -637,6 +642,7 @@ 1CF65AA92A452D9C0042FFBD /* Workout.swift in Sources */, 1CF65AA62A452D9C0042FFBD /* Equipment.swift in Sources */, 1C4AFF162A60F27E0027710B /* ThotStyle.swift in Sources */, + 1C4AFF212A8801090027710B /* AudioQueue.swift in Sources */, 1CF65AB12A452E1A0042FFBD /* BridgeModule.swift in Sources */, 1CF65AAA2A452D9C0042FFBD /* RegisteredUser.swift in Sources */, 1CF65AB62A4532940042FFBD /* WatchMainViewModel.swift in Sources */, diff --git a/Werkout_ios/APIModels/AudioQueue.swift b/Werkout_ios/APIModels/AudioQueue.swift new file mode 100644 index 0000000..5f9ab76 --- /dev/null +++ b/Werkout_ios/APIModels/AudioQueue.swift @@ -0,0 +1,40 @@ +// +// AudioQueue.swift +// Werkout_ios +// +// Created by Trey Tartt on 8/12/23. +// + +import Foundation + +enum AudioType { + case shortBeep + case finishBeep + case remoteURL(URL) +} + +struct AudioQueue: Codable, Identifiable, Equatable { + var id = UUID() + + let playAt: Int + let audioURL: String + + enum CodingKeys: String, CodingKey { + case playAt = "play_at" + case audioURL = "audio_url" + } + + var audioType: AudioType { + if audioURL == "short_beep" { + return .shortBeep + } else if audioURL == "long_beep" { + return .finishBeep + } else { + if let url = URL(string: BaseURLs.currentBaseURL + "/media/" + audioURL) { + return .remoteURL(url) + } else { + return .shortBeep + } + } + } +} diff --git a/Werkout_ios/APIModels/Exercise.swift b/Werkout_ios/APIModels/Exercise.swift index ca06f81..e9d4605 100644 --- a/Werkout_ios/APIModels/Exercise.swift +++ b/Werkout_ios/APIModels/Exercise.swift @@ -21,6 +21,7 @@ struct SupersetExercise: Identifiable, Codable, Equatable, Hashable { let order, superset: Int? let uniqueID: String? let description: String? + let audioQueues: [AudioQueue]? enum CodingKeys: String, CodingKey { case workout, exercise, weight, reps, duration, order, superset, id, description @@ -28,6 +29,7 @@ struct SupersetExercise: Identifiable, Codable, Equatable, Hashable { case weightAudio = "weight_audio" case createdAt = "created_at" case uniqueID = "unique_id" + case audioQueues = "audio_queues" } public func hash(into hasher: inout Hasher) { @@ -49,7 +51,7 @@ struct Exercise: Identifiable, Codable, Equatable { let isDistance, isDuration, isReps: Bool let jointsUsed, movementPatterns, equipmentRequired, muscleGroups: String let synonyms: String? - + enum CodingKeys: String, CodingKey { case id, muscles, equipment case audioURL = "audio_url" diff --git a/Werkout_ios/BaseURLs.swift b/Werkout_ios/BaseURLs.swift index c43c8e6..537f209 100644 --- a/Werkout_ios/BaseURLs.swift +++ b/Werkout_ios/BaseURLs.swift @@ -12,6 +12,6 @@ enum BaseURLs: String { case dev = "https://dev.werkout.fitness" static var currentBaseURL: String { - return BaseURLs.dev.rawValue + return BaseURLs.local.rawValue } } diff --git a/Werkout_ios/BridgeModule.swift b/Werkout_ios/BridgeModule.swift index 0ab1903..54045e8 100644 --- a/Werkout_ios/BridgeModule.swift +++ b/Werkout_ios/BridgeModule.swift @@ -54,6 +54,7 @@ class BridgeModule: NSObject, ObservableObject { public private(set) var heartRates: [Int]? var audioPlayer: AVAudioPlayer? + var avPlayer: AVPlayer? func start(workout: Workout) { currentExerciseInfo.complete = { @@ -130,11 +131,23 @@ class BridgeModule: NSObject, ObservableObject { } @objc func updateCurrentExerciseTimer() { - if currentExerciseTimeLeft > 0 { + if currentExerciseTimeLeft > 1 { currentExerciseTimeLeft -= 1 - if currentExerciseTimeLeft <= 3 { - playBeep() + if let currentExercise = currentExerciseInfo.allSupersetExecercise, let audioQueues = currentExercise.audioQueues { + if let audioQueue = audioQueues.first(where: { + $0.playAt == currentExerciseTimeLeft + }) { + switch audioQueue.audioType { + + case .shortBeep: + playBeep() + case .finishBeep: + playFinished() + case .remoteURL(let url): + playRemoteAudio(fromURL: url) + } + } } sendCurrentExerciseToWatch() } else { @@ -154,7 +167,6 @@ class BridgeModule: NSObject, ObservableObject { func nextExercise() { if let nextSupersetExercise = currentExerciseInfo.nextExercise { updateCurrent(exercise: nextSupersetExercise) - playFinished() } else { completeWorkout() } @@ -205,6 +217,23 @@ class BridgeModule: NSObject, ObservableObject { } } + func playRemoteAudio(fromURL url: URL) { +#if os(iOS) + let playerItem = AVPlayerItem(url: url) + do { + try AVAudioSession.sharedInstance().setCategory(.playback, + mode: .default, + options: [.mixWithOthers]) + try AVAudioSession.sharedInstance().setActive(true) + + avPlayer = AVPlayer(playerItem: playerItem) + avPlayer?.play() + } catch { + print("ERROR") + } +#endif + } + func playBeep() { #if os(iOS) if let path = Bundle.main.path(forResource: "short_beep", ofType: "m4a") { @@ -283,6 +312,7 @@ extension BridgeModule: WCSessionDelegate { switch model { case .nextExercise: nextExercise() + playFinished() case .workoutComplete(let data): let model = try! JSONDecoder().decode(WatchFinishWorkoutModel.self, from: data) totalCaloire = Float(model.totalBurnedEnergery) diff --git a/Werkout_ios/CurrentWorkoutInfo.swift b/Werkout_ios/CurrentWorkoutInfo.swift index f4d50b0..6c99ee8 100644 --- a/Werkout_ios/CurrentWorkoutInfo.swift +++ b/Werkout_ios/CurrentWorkoutInfo.swift @@ -33,6 +33,11 @@ class CurrentWorkoutInfo { return exercise } + var allSupersetExecercise: SupersetExercise? { + let obj = workout?.allSupersetExecercise?[allSupersetExecerciseIndex] + return obj + } + var nextExercise: SupersetExercise? { guard let workout = workout else { return nil } guard let supersets = workout.supersets else { return nil }