diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj index 12d7237..4870371 100644 --- a/LeCountdown.xcodeproj/project.pbxproj +++ b/LeCountdown.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ C4060DF5297AE9A7003FAB80 /* TimeInterval+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */; }; C4060DF7297AFEF2003FAB80 /* NewCountdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4060DF6297AFEF2003FAB80 /* NewCountdownView.swift */; }; C40FDB622992985C0042A390 /* TextToSpeechRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FDB612992985C0042A390 /* TextToSpeechRecorder.swift */; }; + C415D3C829BF6D360037B215 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C415D3CA29BF6D360037B215 /* InfoPlist.strings */; }; C438C7C12980228B00BF3EF9 /* CountdownScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7C02980228B00BF3EF9 /* CountdownScheduler.swift */; }; C438C7C5298024E900BF3EF9 /* NSManagedContext+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7C4298024E900BF3EF9 /* NSManagedContext+Extensions.swift */; }; C438C7C929803CA000BF3EF9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7C829803CA000BF3EF9 /* AppDelegate.swift */; }; @@ -280,6 +281,7 @@ C4060DF6297AFEF2003FAB80 /* NewCountdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCountdownView.swift; sourceTree = ""; }; C40FDB612992985C0042A390 /* TextToSpeechRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextToSpeechRecorder.swift; sourceTree = ""; }; C40FDB672993D5E80042A390 /* LeCountdown.0.4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.4.xcdatamodel; sourceTree = ""; }; + C415D3C929BF6D360037B215 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; C418A14F298428CB00C22230 /* LeCountdown.0.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.1.xcdatamodel; sourceTree = ""; }; C438C7C02980228B00BF3EF9 /* CountdownScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountdownScheduler.swift; sourceTree = ""; }; C438C7C4298024E900BF3EF9 /* NSManagedContext+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedContext+Extensions.swift"; sourceTree = ""; }; @@ -499,6 +501,7 @@ C4060DC3297AE73D003FAB80 /* Assets.xcassets */, C438C80429813B3100BF3EF9 /* LeCountdown.entitlements */, C473C32A29AA330E0056B38A /* Localizable.strings */, + C415D3CA29BF6D360037B215 /* InfoPlist.strings */, C4E5D66F29B753D7008E7465 /* AppShortcuts.strings */, C4060DCD297AE73D003FAB80 /* Info.plist */, C4060DC5297AE73D003FAB80 /* Preview Content */, @@ -937,6 +940,7 @@ buildActionMask = 2147483647; files = ( C4060DC7297AE73D003FAB80 /* Preview Assets.xcassets in Resources */, + C415D3C829BF6D360037B215 /* InfoPlist.strings in Resources */, C4BA2AEA2995AD1C00CB4FBA /* SEM_Synths_Loop4_Nothing_Like_You.wav in Resources */, C473C32C29AA330E0056B38A /* Localizable.strings in Resources */, C4E5D68229B93583008E7465 /* PVP_Stab_Oneshot_Bleep_Em.wav in Resources */, @@ -1231,6 +1235,14 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + C415D3CA29BF6D360037B215 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + C415D3C929BF6D360037B215 /* fr */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; C473C31C29A926F50056B38A /* LaunchWidget.intentdefinition */ = { isa = PBXVariantGroup; children = ( @@ -1390,7 +1402,7 @@ ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = LeCountdown/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Momo; + INFOPLIST_KEY_CFBundleDisplayName = Enchant; INFOPLIST_KEY_NSAppleMusicUsageDescription = NSAppleMusicUsageDescription; INFOPLIST_KEY_NSSupportsLiveActivities = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -1424,7 +1436,7 @@ ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = LeCountdown/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = Momo; + INFOPLIST_KEY_CFBundleDisplayName = Enchant; INFOPLIST_KEY_NSAppleMusicUsageDescription = NSAppleMusicUsageDescription; INFOPLIST_KEY_NSSupportsLiveActivities = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; diff --git a/LeCountdown/Conductor.swift b/LeCountdown/Conductor.swift index 809cd5e..852c7dd 100644 --- a/LeCountdown/Conductor.swift +++ b/LeCountdown/Conductor.swift @@ -280,15 +280,23 @@ class Conductor: ObservableObject { } func playSound(_ sound: Sound) { - self._playSound(sound.soundName) + self._playSound(sound.fileName) } - fileprivate func _playSound(_ filename: String) { + func playSound(_ sound: Sound, duration: TimeInterval) { + self._playSound(sound.fileName, duration: duration) + } + + fileprivate func _playSound(_ filename: String, duration: TimeInterval? = nil) { do { let soundFile = try SoundFile(fullName: filename) let soundPlayer = SoundPlayer() self.soundPlayer = soundPlayer - try soundPlayer.playSound(soundFile: soundFile, repeats: false) + if let duration { + try soundPlayer.play(soundFile: soundFile, for: duration) + } else { + try soundPlayer.playSound(soundFile: soundFile) + } } catch { Logger.error(error) // TODO: manage error diff --git a/LeCountdown/Model/Model+Extensions.swift b/LeCountdown/Model/Model+Extensions.swift index 25177de..8b23666 100644 --- a/LeCountdown/Model/Model+Extensions.swift +++ b/LeCountdown/Model/Model+Extensions.swift @@ -41,7 +41,7 @@ extension AbstractSoundTimer { } var soundName: String { - return self.someSound.soundName + return self.someSound.fileName } } diff --git a/LeCountdown/Sound/Sound.swift b/LeCountdown/Sound/Sound.swift index 48983a1..dfae47a 100644 --- a/LeCountdown/Sound/Sound.swift +++ b/LeCountdown/Sound/Sound.swift @@ -88,7 +88,7 @@ enum Sound: Int, CaseIterable, Identifiable, Localized { } } - var soundName: String { + var fileName: String { switch self { case .trainhorn: return "train_horn.mp3" case .forestStream: return "forest_stream.mp3" @@ -111,20 +111,27 @@ enum Sound: Int, CaseIterable, Identifiable, Localized { } } + var isRestricted: Bool { + switch self { + case .trainhorn, .forestStream, .sbSEM_Synths_Loop4_Nothing_Like_You: + return false + default: + return true + } + } + var url: URL? { - - let components = self.soundName.components(separatedBy: ".") + let components = self.fileName.components(separatedBy: ".") if components.count == 2 { return Bundle.main.url(forResource: components[0], withExtension: components[1]) } else { print("bad sound file name for \(self)") return nil } - } func soundFile() throws -> SoundFile { - return try SoundFile(fullName: self.soundName) + return try SoundFile(fullName: self.fileName) } func duration() async throws -> TimeInterval { diff --git a/LeCountdown/Sound/SoundPlayer.swift b/LeCountdown/Sound/SoundPlayer.swift index a06c5df..21a8fae 100644 --- a/LeCountdown/Sound/SoundPlayer.swift +++ b/LeCountdown/Sound/SoundPlayer.swift @@ -37,19 +37,26 @@ enum SoundPlayerError : Error { fileprivate var _player: AVAudioPlayer? - func playSound(soundFile: SoundFile, repeats: Bool) throws { + fileprivate var _timer: Timer? = nil + + func play(soundFile: SoundFile, for duration: TimeInterval) throws { + try self.playSound(soundFile: soundFile) + self._timer = Timer(timeInterval: duration, repeats: false, block: { _ in + self._player?.stop() + }) + } + + func playSound(soundFile: SoundFile) throws { guard let url = soundFile.url else { throw SoundPlayerError.missingResourceError(file: soundFile) } -// let audioSession: AVAudioSession = AVAudioSession.sharedInstance() -// try audioSession.setCategory(.playback) -// try audioSession.setActive(true) + let audioSession: AVAudioSession = AVAudioSession.sharedInstance() + try audioSession.setCategory(.playback) + try audioSession.setActive(true) _player = try AVAudioPlayer(contentsOf: url) _player?.prepareToPlay() -// let loopCount = repeats ? Int.max : 0 -// _player?.numberOfLoops = 0 //loopCount _player?.volume = 1.0 _player?.delegate = self diff --git a/LeCountdown/Subscription/AppGuard.swift b/LeCountdown/Subscription/AppGuard.swift index b26d1c3..8ef3999 100644 --- a/LeCountdown/Subscription/AppGuard.swift +++ b/LeCountdown/Subscription/AppGuard.swift @@ -12,16 +12,16 @@ public enum StoreError: Error { case failedVerification } -enum StorePlan : String, CaseIterable { +enum StorePlan: String, CaseIterable { case none - case unlimited = "com.staxriver.lecountdown.unlimited" + case unlimited = "com.staxriver.enchant.unlimited" } extension Notification.Name { static let StoreEventHappened = Notification.Name("storeEventHappened") } -@objc class AppGuard : NSObject { +@objc class AppGuard: NSObject { static var main: AppGuard = AppGuard() diff --git a/LeCountdown/Views/Reusable/SoundSelectionView.swift b/LeCountdown/Views/Reusable/SoundSelectionView.swift index d5eb106..3652ec1 100644 --- a/LeCountdown/Views/Reusable/SoundSelectionView.swift +++ b/LeCountdown/Views/Reusable/SoundSelectionView.swift @@ -14,7 +14,6 @@ struct PlaylistsView: View { var body: some View { Form { - ForEach(Playlist.selectable) { playlist in PlaylistSectionView(playlist: playlist) .environmentObject(self.model) @@ -46,11 +45,16 @@ struct PlaylistSectionView: View { self._playSound(sound) } Spacer() - RightAlignToggleRow(item: sound, selected: self.model.binding(sound: sound), keyPath: \.formattedDuration) { selected in - self.model.selectSound(sound, selected: selected) - }.frame(width: 120.0) - .foregroundColor(.gray) - .font(.caption) + if AppGuard.main.isAuthorized { + RightAlignToggleRow(item: sound, selected: self.model.binding(sound: sound), keyPath: \.formattedDuration) { selected in + self.model.selectSound(sound, selected: selected) + }.frame(width: 120.0) + .foregroundColor(.gray) + .font(.caption) + } else { + Image(systemName: "lock.circle.fill") + } + } } } header: { @@ -62,7 +66,11 @@ struct PlaylistSectionView: View { } fileprivate func _playSound(_ sound: Sound) { - Conductor.maestro.playSound(sound) + if AppGuard.main.isAuthorized { + Conductor.maestro.playSound(sound) + } else { + Conductor.maestro.playSound(sound, duration: 30.0) + } } } diff --git a/LeCountdown/fr.lproj/InfoPlist.strings b/LeCountdown/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000..1c764e5 --- /dev/null +++ b/LeCountdown/fr.lproj/InfoPlist.strings @@ -0,0 +1,8 @@ +/* + InfoPlist.strings + LeCountdown + + Created by Laurent Morvillier on 13/03/2023. + +*/ +"CFBundleDisplayName" = "Enchante";