Changing notification to player to trigger audio

main
Laurent 3 years ago
parent 99d55b3c3c
commit b99fb2a59a
  1. 8
      LeCountdown.xcodeproj/project.pbxproj
  2. 22
      LeCountdown/Conductor.swift
  3. 37
      LeCountdown/CountdownScheduler.swift
  4. 9
      LeCountdown/Info.plist
  5. 5
      LeCountdown/Model/Model+Extensions.swift
  6. 52
      LeCountdown/Sound/DelaySoundPlayer.swift
  7. 32
      LeCountdown/Views/DialView.swift

@ -160,6 +160,9 @@
C4E5D66729B73AED008E7465 /* TimerIdentifierAppEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D66529B73AED008E7465 /* TimerIdentifierAppEntity.swift */; };
C4E5D66A29B73FC6008E7465 /* TimerShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D66929B73FC6008E7465 /* TimerShortcuts.swift */; };
C4E5D66D29B753D7008E7465 /* AppShortcuts.strings in Resources */ = {isa = PBXBuildFile; fileRef = C4E5D66F29B753D7008E7465 /* AppShortcuts.strings */; };
C4E5D67429B88734008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4E5D67729B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4E5D67829B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; };
C4F8B15729891271005C86A5 /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; };
C4F8B15929891528005C86A5 /* forest_stream.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C4F8B15829891528005C86A5 /* forest_stream.mp3 */; };
@ -371,6 +374,7 @@
C4E5D66929B73FC6008E7465 /* TimerShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerShortcuts.swift; sourceTree = "<group>"; };
C4E5D66E29B753D7008E7465 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AppShortcuts.strings; sourceTree = "<group>"; };
C4E5D67029B753DC008E7465 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppShortcuts.strings; sourceTree = "<group>"; };
C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelaySoundPlayer.swift; sourceTree = "<group>"; };
C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = "<group>"; };
C4F8B15829891528005C86A5 /* forest_stream.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = forest_stream.mp3; sourceTree = "<group>"; };
C4F8B15E298961A7005C86A5 /* ReorderableForEach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableForEach.swift; sourceTree = "<group>"; };
@ -626,6 +630,7 @@
children = (
C445FA912987CC8A0054D761 /* Sound.swift */,
C445FA8E2987B83B0054D761 /* SoundPlayer.swift */,
C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */,
);
path = Sound;
sourceTree = "<group>";
@ -1002,6 +1007,7 @@
C4BA2B6829A3C4AC00CB4FBA /* Context+Calculations.swift in Sources */,
C4BA2B04299A42EF00CB4FBA /* NewDataView.swift in Sources */,
C4BA2B4C299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4E5D67429B88734008E7465 /* DelaySoundPlayer.swift in Sources */,
C438C7FF2981300500BF3EF9 /* IntentDataProvider.swift in Sources */,
C4E5D66629B73AED008E7465 /* StartTimerIntent.swift in Sources */,
C4BA2AD62993F62700CB4FBA /* SoundSelectionView.swift in Sources */,
@ -1107,6 +1113,7 @@
C4BA2B4A299FCE0C00CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */,
C4BA2B4D299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4F8B194298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */,
C4E5D67729B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */,
C438C8152982BD9000BF3EF9 /* IntentDataProvider.swift in Sources */,
C473C31929A926F50056B38A /* LaunchWidget.intentdefinition in Sources */,
C4F8B15B29892D40005C86A5 /* SoundPlayer.swift in Sources */,
@ -1175,6 +1182,7 @@
C473C2F429A8DAE70056B38A /* Model+Extensions.swift in Sources */,
C445FA86298448720054D761 /* CoolPic.swift in Sources */,
C4BA2B4E299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4E5D67829B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */,
C4BA2B23299BE82E00CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
C473C2F529A8DAF30056B38A /* PropertyWrappers.swift in Sources */,
C473C2F029A8CFFC0056B38A /* TimerRouter.swift in Sources */,

@ -21,6 +21,7 @@ class Conductor: ObservableObject {
static let maestro: Conductor = Conductor()
@Published var soundPlayer: SoundPlayer? = nil
@Published var delaySoundPlayer: DelaySoundPlayer? = nil
@UserDefault(PreferenceKey.countdowns.rawValue, defaultValue: [:]) static var savedCountdowns: [String : DateInterval]
@UserDefault(PreferenceKey.stopwatches.rawValue, defaultValue: [:]) static var savedStopwatches: [String : Date]
@ -148,6 +149,25 @@ class Conductor: ObservableObject {
}
}
func prepareAlarm(countdown: Countdown, handler: @escaping (Result<Date?, Error>) -> Void) {
DispatchQueue.main.async {
do {
let date = Date(timeIntervalSinceNow: countdown.duration)
let soundFile = try SoundFile(fullName: countdown.soundName)
self.delaySoundPlayer = DelaySoundPlayer(soundFile: soundFile)
try self.delaySoundPlayer?.start(in: countdown.duration,
repeatCount: Int(countdown.repeatCount))
self.startCountdown(date, countdown: countdown)
handler(.success(date))
} catch {
handler(.failure(error))
}
}
}
// MARK: - Stopwatch
func startStopwatch(_ stopwatch: Stopwatch) {
@ -199,7 +219,7 @@ class Conductor: ObservableObject {
let timer = context.object(stringId: timerId)
switch timer {
case let cd as Countdown:
coolSound = cd.coolSound
coolSound = cd.someSound
case let sw as Stopwatch:
coolSound = sw.coolSound
default:

@ -16,6 +16,7 @@ class CountdownScheduler {
func scheduleIfPossible(countdown: Countdown, handler: @escaping (Result<Date?, Error>) -> Void) {
self.cancelCurrentNotifications(countdownId: countdown.stringId)
Conductor.maestro.prepareAlarm(countdown: countdown, handler: handler)
self._scheduleCountdownNotification(countdown: countdown, handler: handler)
}
@ -36,20 +37,20 @@ class CountdownScheduler {
content.body = body
let sound = countdown.soundName
self._createNotification(countdown: countdown, content: content, handler: handler)
print("Selected sound = \(sound)")
content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(rawValue: sound), withAudioVolume: 1.0)
content.interruptionLevel = .critical
content.relevanceScore = 1.0
// content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(rawValue: sound), withAudioVolume: 1.0)
// content.interruptionLevel = .critical
// content.relevanceScore = 1.0
let notificationCount = 1 + countdown.repeatCount
// let notificationCount = 1 + countdown.repeatCount
// self._createNotification(countdown: countdown, content: content, handler: handler)
for i in 0..<notificationCount {
let offset = Double(i) * 10.0 // every 30 seconds
self._createNotification(countdown: countdown, offset: offset, content: content, handler: handler)
}
// for i in 0..<notificationCount {
// let offset = Double(i) * 10.0 // every 30 seconds
// self._createNotification(countdown: countdown, offset: offset, content: content, handler: handler)
// }
}
@ -70,15 +71,15 @@ class CountdownScheduler {
print("Scheduling error = \(error)")
} else {
if offset == 0.0 {
if let triggerDate = trigger.nextTriggerDate() {
Conductor.maestro.startCountdown(triggerDate, countdown: countdown)
handler(.success(triggerDate))
} else {
let backupDate = Date().addingTimeInterval(duration)
Conductor.maestro.startCountdown(backupDate, countdown: countdown)
}
}
// if offset == 0.0 {
// if let triggerDate = trigger.nextTriggerDate() {
// Conductor.maestro.startCountdown(triggerDate, countdown: countdown)
// handler(.success(triggerDate))
// } else {
// let backupDate = Date().addingTimeInterval(duration)
// Conductor.maestro.startCountdown(backupDate, countdown: countdown)
// }
// }
}
}
}

@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.staxriver.lecountdown.refresh</string>
</array>
<key>INAlternativeAppNames</key>
<array>
<array>
@ -15,10 +19,6 @@
<string>Gogo</string>
</dict>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.staxriver.lecountdown.refresh</string>
</array>
<key>NSUserActivityTypes</key>
<array>
<string>LaunchTimerIntent</string>
@ -35,7 +35,6 @@
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
</array>
</dict>
</plist>

@ -9,7 +9,6 @@ import Foundation
import SwiftUI
import CoreData
extension AbstractSoundTimer {
var sounds: Set<Sound> {
@ -23,7 +22,7 @@ extension AbstractSoundTimer {
self.soundList = sounds.stringRepresentation
}
var coolSound: Sound {
var someSound: Sound {
var sounds = self.sounds
// remove last played sound if the playlist has at least 3 sounds
@ -42,7 +41,7 @@ extension AbstractSoundTimer {
}
var soundName: String {
return self.coolSound.soundName
return self.someSound.soundName
}
}

@ -0,0 +1,52 @@
//
// BackgroundSoundPlayer.swift
// LeCountdown
//
// Created by Laurent Morvillier on 08/03/2023.
//
import Foundation
import AVFoundation
@objc class DelaySoundPlayer: NSObject, AVAudioPlayerDelegate {
fileprivate var _player: AVAudioPlayer?
private var soundFile: SoundFile
init(soundFile: SoundFile) {
self.soundFile = soundFile
}
func start(in duration: TimeInterval, repeatCount: Int) throws {
guard let url = self.soundFile.url else {
throw SoundPlayerError.missingResourceError(file: self.soundFile)
}
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback)
try audioSession.setActive(true)
let player = try AVAudioPlayer(contentsOf: url)
_player = player
_player?.prepareToPlay()
_player?.volume = 1.0
_player?.delegate = self
player.numberOfLoops = repeatCount
_player?.play(atTime: player.deviceCurrentTime + duration)
}
func stop() {
self._player?.stop()
}
// MARK: - Delegate
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
Logger.log("audioPlayerDidFinishPlaying: successfully = \(flag)")
}
}

@ -17,7 +17,7 @@ struct DialView: View {
@State var timer: AbstractTimer
var isEditingBinding: Binding<Bool>
@State var showSilentModeAlert: Bool = false
// @State var showSilentModeAlert: Bool = false
var frameSize: CGFloat
@ -33,11 +33,13 @@ struct DialView: View {
switch self.isEditingBinding.wrappedValue {
case false:
Button {
if Preferences.hideSilentModeAlerts {
self._launchTimer()
} else {
self.showSilentModeAlert = true
}
self._launchTimer()
// if Preferences.hideSilentModeAlerts {
// self._launchTimer()
// } else {
// self.showSilentModeAlert = true
// }
} label: {
VStack {
Spacer()
@ -79,15 +81,15 @@ struct DialView: View {
}
.frame(width: frameSize, height: 80.0)
.cornerRadius(20.0)
.alert("Make sure your device is not on silent mode", isPresented: $showSilentModeAlert) {
Button("OK", role: .cancel) {
self._launchTimer()
}
Button("Don't show this again", role: .destructive) {
self._launchTimer()
Preferences.hideSilentModeAlerts(true)
}
}
// .alert("Make sure your device is not on silent mode", isPresented: $showSilentModeAlert) {
// Button("OK", role: .cancel) {
// self._launchTimer()
// }
// Button("Don't show this again", role: .destructive) {
// self._launchTimer()
// Preferences.hideSilentModeAlerts(true)
// }
// }
}
@ViewBuilder

Loading…
Cancel
Save