Laurent 3 years ago
parent 2a09dc6593
commit 5d6bdb8c96
  1. 16
      LeCountdown.xcodeproj/project.pbxproj
  2. 14
      LeCountdown/Conductor.swift
  3. 2
      LeCountdown/Model/Model+Extensions.swift
  4. 17
      LeCountdown/Sound/Sound.swift
  5. 19
      LeCountdown/Sound/SoundPlayer.swift
  6. 6
      LeCountdown/Subscription/AppGuard.swift
  7. 22
      LeCountdown/Views/Reusable/SoundSelectionView.swift
  8. 8
      LeCountdown/fr.lproj/InfoPlist.strings

@ -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 = "<group>"; };
C40FDB612992985C0042A390 /* TextToSpeechRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextToSpeechRecorder.swift; sourceTree = "<group>"; };
C40FDB672993D5E80042A390 /* LeCountdown.0.4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.4.xcdatamodel; sourceTree = "<group>"; };
C415D3C929BF6D360037B215 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C418A14F298428CB00C22230 /* LeCountdown.0.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.1.xcdatamodel; sourceTree = "<group>"; };
C438C7C02980228B00BF3EF9 /* CountdownScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountdownScheduler.swift; sourceTree = "<group>"; };
C438C7C4298024E900BF3EF9 /* NSManagedContext+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedContext+Extensions.swift"; sourceTree = "<group>"; };
@ -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 = "<group>";
};
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;

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

@ -41,7 +41,7 @@ extension AbstractSoundTimer {
}
var soundName: String {
return self.someSound.soundName
return self.someSound.fileName
}
}

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

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

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

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

@ -0,0 +1,8 @@
/*
InfoPlist.strings
LeCountdown
Created by Laurent Morvillier on 13/03/2023.
*/
"CFBundleDisplayName" = "Enchante";
Loading…
Cancel
Save