Fixes Siri flow

main
Laurent 3 years ago
parent 3feb133a79
commit c398b31944
  1. 34
      LeCountdown/Conductor.swift
  2. 28
      LeCountdown/Intent/StartTimerIntent.swift
  3. 36
      LeCountdown/Intent/TimerIdentifierAppEntity.swift
  4. 13
      LeCountdown/Sound/DelaySoundPlayer.swift
  5. 16
      LeCountdown/TimerRouter.swift
  6. 6
      LeCountdown/Utils/AppError.swift
  7. 6
      LeCountdown/Widget/IntentDataProvider.swift

@ -166,26 +166,6 @@ class Conductor: ObservableObject {
do {
let end = try self._scheduleSoundPlayer(countdown: countdown, in: countdown.duration)
// let start = Date()
// let end = start.addingTimeInterval(countdown.duration)
// FileLogger.log("schedule countdown \(self._timerName(countdownId)) at \(end)")
//
// let sound = countdown.someSound
// let soundPlayer = try DelaySoundPlayer(timerID: countdownId, sound: sound)
//
// FileLogger.log("a) self._delayedSoundPlayers count = \(self._delayedSoundPlayers.count)")
// self._delayedSoundPlayers[countdownId] = soundPlayer
// FileLogger.log("b) self._delayedSoundPlayers count = \(self._delayedSoundPlayers.count)")
//
// try soundPlayer.start(in: countdown.duration,
// repeatCount: Int(countdown.repeatCount))
//
// let dateInterval = DateInterval(start: start, end: end)
// self.currentCountdowns[countdownId] = dateInterval
//
// self._launchLiveActivity(timer: countdown, date: end)
if Preferences.playConfirmationSound {
self._playConfirmationSound(timer: countdown)
}
@ -306,18 +286,22 @@ class Conductor: ObservableObject {
// MARK: - Stopwatch
func startStopwatch(_ stopwatch: Stopwatch) {
func startStopwatch(_ stopwatchId: TimerID) {
DispatchQueue.main.async {
let lsw: LiveStopWatch = LiveStopWatch(start: Date())
self.currentStopwatches[stopwatch.stringId] = lsw
guard let stopWatch = IntentDataProvider.main.timer(id: stopwatchId) as? Stopwatch else {
return
}
let lsw: LiveStopWatch = LiveStopWatch(start: Date())
self.currentStopwatches[stopWatch.stringId] = lsw
if Preferences.playConfirmationSound {
self._playSound(Const.confirmationSound.rawValue)
}
self._endLiveActivity(timerId: stopwatch.stringId)
self._launchLiveActivity(timer: stopwatch, date: lsw.start)
self._endLiveActivity(timerId: stopWatch.stringId)
self._launchLiveActivity(timer: stopWatch, date: lsw.start)
}
}

@ -8,6 +8,7 @@
import Foundation
import AppIntents
import SwiftUI
import CoreData
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
struct StartTimerIntent: AudioStartingIntent {
@ -31,29 +32,38 @@ struct StartTimerIntent: AudioStartingIntent {
if let timer {
timerIdentifier = timer
} else {
timerIdentifier = try await $timer.requestDisambiguation(among: timerEntities())
let entities = await _timerEntities()
timerIdentifier = try await $timer.requestDisambiguation(among: entities)
}
if let abstractTimer = IntentDataProvider.main.timer(id: timerIdentifier.id) {
if let abstractTimer = await _timer(id: timerIdentifier.id) {
do {
_ = try await TimerRouter.performAction(timer: abstractTimer)
return .result(value: 1)
} catch {
Logger.error(error)
// let dialog: IntentDialog = "\(error.localizedDescription)"
throw error
}
} else {
throw AppError.timerNotFound(id: timerIdentifier.id)
}
}
fileprivate func _timerEntities() async -> [TimerIdentifierAppEntity] {
await PersistenceController.shared.container.performBackgroundTask { context in
do {
let timers: [AbstractTimer] = try IntentDataProvider.main.timers(context: context)
return timers.map { TimerIdentifierAppEntity(id: $0.stringId, displayString: $0.displayName) }
} catch {
return []
}
}
}
func timerEntities() -> [TimerIdentifierAppEntity] {
do {
let timers: [AbstractTimer] = try IntentDataProvider.main.timers()
return timers.map { TimerIdentifierAppEntity(id: $0.stringId, displayString: $0.displayName) }
} catch {
return []
fileprivate func _timer(id: String) async -> AbstractTimer? {
await PersistenceController.shared.container.performBackgroundTask { context in
return IntentDataProvider.main.timer(context: context, id: id)
}
}

@ -10,39 +10,37 @@ import AppIntents
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
struct TimerIdentifierAppEntity: AppEntity {
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Timer Identifier")
static var defaultQuery = TimerIdentifierAppEntityQuery()
var id: String // if your identifier is not a String, conform the entity to EntityIdentifierConvertible.
var displayString: String
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(displayString)")
}
init(id: String, displayString: String) {
self.id = id
self.displayString = displayString
}
struct TimerIdentifierAppEntityQuery: EntityQuery {
func entities(for identifiers: [TimerIdentifierAppEntity.ID]) async throws -> [TimerIdentifierAppEntity] {
let timers = identifiers.compactMap { IntentDataProvider.main.timer(id: $0) }
return timers.map { TimerIdentifierAppEntity(id: $0.stringId, displayString: $0.displayName) }
// TODO: return TimerIdentifierAppEntity entities with the specified identifiers here.
await PersistenceController.shared.container.performBackgroundTask { context in
let timers = identifiers.compactMap { IntentDataProvider.main.timer(context: context, id: $0) }
return timers.map { TimerIdentifierAppEntity(id: $0.stringId, displayString: $0.displayName) }
}
}
func suggestedEntities() async throws -> [TimerIdentifierAppEntity] {
try await PersistenceController.shared.container.performBackgroundTask { context in
let timers = try IntentDataProvider.main.timers(context: context)
return timers.map { TimerIdentifierAppEntity(id: $0.stringId, displayString: $0.displayName) }
}
// TODO: return likely TimerIdentifierAppEntity entities here.
// This method is optional; the default implementation returns an empty array.
}
}
static var defaultQuery = TimerIdentifierAppEntityQuery()
var id: String // if your identifier is not a String, conform the entity to EntityIdentifierConvertible.
var displayString: String
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(displayString)")
}
init(id: String, displayString: String) {
self.id = id
self.displayString = displayString
}
}

@ -39,6 +39,8 @@ import AVFoundation
fileprivate func _play(in duration: TimeInterval, repeatCount: Int) throws {
self._activateAudioSession()
self._player.prepareToPlay()
self._player.volume = 1.0
self._player.delegate = self
@ -70,4 +72,15 @@ import AVFoundation
self.stop()
}
fileprivate func _activateAudioSession() {
Logger.log("_activateAudioSession")
do {
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback, options: .duckOthers)
try audioSession.setActive(true)
} catch {
Logger.error(error)
}
}
}

@ -33,9 +33,9 @@ class TimerRouter {
static func performAction(timer: AbstractTimer, handler: @escaping (Result<Void, Error>) -> Void) {
switch timer {
case let countdown as Countdown:
self._launchCountdown(countdown, handler: handler)
self._launchCountdown(countdown.stringId, handler: handler)
case let stopwatch as Stopwatch:
self._startStopwatch(stopwatch, handler: handler)
self._startStopwatch(stopwatch.stringId, handler: handler)
case let alarm as Alarm:
self._scheduleAlarm(alarm, handler: handler)
default:
@ -45,11 +45,17 @@ class TimerRouter {
}
fileprivate static func _launchCountdown(_ countdown: Countdown, handler: @escaping (Result<Void, Error>) -> Void) {
fileprivate static func _launchCountdown(_ countdownId: TimerID, handler: @escaping (Result<Void, Error>) -> Void) {
UNUserNotificationCenter.current().getNotificationSettings { settings in
DispatchQueue.main.async {
guard let countdown = IntentDataProvider.main.timer(id: countdownId) as? Countdown else {
handler(.failure(AppError.timerNotFound(id: countdownId)))
return
}
CountdownScheduler.master.scheduleIfPossible(countdown: countdown) { result in
switch result {
case .success:
@ -67,8 +73,8 @@ class TimerRouter {
}
fileprivate static func _startStopwatch(_ stopwatch: Stopwatch, handler: @escaping (Result<Void, Error>) -> Void) {
Conductor.maestro.startStopwatch(stopwatch)
fileprivate static func _startStopwatch(_ stopwatchId: TimerID, handler: @escaping (Result<Void, Error>) -> Void) {
Conductor.maestro.startStopwatch(stopwatchId)
handler(.success(Void()))
}

@ -11,6 +11,7 @@ enum AppError: LocalizedError {
case defaultError(error: Error)
case timerNotFound(id: String)
case timerNotManaged(timer: AbstractTimer)
var errorDescription: String? {
switch self {
@ -18,6 +19,9 @@ enum AppError: LocalizedError {
return error.localizedDescription
case .timerNotFound(let id):
return "Timer not found: \(id)"
case .timerNotManaged(let timer):
return "Timer not managed: \(timer.stringId)"
}
}
@ -27,6 +31,8 @@ enum AppError: LocalizedError {
return error.localizedDescription
case .timerNotFound:
return "Timer not found"
case .timerNotManaged(let timer):
return "Timer not managed"
}
}

@ -22,9 +22,13 @@ class IntentDataProvider {
return try context.fetch(request)
}
func timer(context: NSManagedObjectContext, id: String) -> AbstractTimer? {
return context.object(stringId: id)
}
func timer(id: String) -> AbstractTimer? {
let context = PersistenceController.shared.container.viewContext
return context.object(stringId: id)
return self.timer(context: context, id: id)
}
}

Loading…
Cancel
Save