|
|
|
|
@ -40,7 +40,6 @@ class Conductor: ObservableObject { |
|
|
|
|
fileprivate var beats: Timer? = nil |
|
|
|
|
|
|
|
|
|
@UserDefault(PreferenceKey.countdowns.rawValue, defaultValue: [:]) static var savedCountdowns: [String : CountdownSequence] |
|
|
|
|
// @UserDefault(PreferenceKey.pausedCountdowns.rawValue, defaultValue: [:]) static var savedPausedCountdowns: [String : Date] |
|
|
|
|
@UserDefault(PreferenceKey.stopwatches.rawValue, defaultValue: [:]) static var savedStopwatches: [String : LiveStopWatch] |
|
|
|
|
|
|
|
|
|
@Published private (set) var liveTimers: [LiveTimer] = [] |
|
|
|
|
@ -50,7 +49,6 @@ class Conductor: ObservableObject { |
|
|
|
|
init() { |
|
|
|
|
self.currentCountdowns = Conductor.savedCountdowns |
|
|
|
|
self.currentStopwatches = Conductor.savedStopwatches |
|
|
|
|
// self.pausedCountdowns = Conductor.savedPausedCountdowns |
|
|
|
|
|
|
|
|
|
self.beats = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { _ in |
|
|
|
|
self._cleanupCountdowns() |
|
|
|
|
@ -63,22 +61,12 @@ class Conductor: ObservableObject { |
|
|
|
|
@Published var currentCountdowns: [String : CountdownSequence] = [:] { |
|
|
|
|
didSet { |
|
|
|
|
Conductor.savedCountdowns = currentCountdowns |
|
|
|
|
// Logger.log("**** currentCountdowns didSet, count = \(currentCountdowns.count)") |
|
|
|
|
withAnimation { |
|
|
|
|
self._buildLiveTimers() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// @Published var pausedCountdowns: [String : Date] = [:] { |
|
|
|
|
// didSet { |
|
|
|
|
// Conductor.savedPausedCountdowns = pausedCountdowns |
|
|
|
|
// withAnimation { |
|
|
|
|
// self._buildLiveTimers() |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
@Published var currentStopwatches: [String : LiveStopWatch] = [:] { |
|
|
|
|
didSet { |
|
|
|
|
Conductor.savedStopwatches = currentStopwatches |
|
|
|
|
@ -121,11 +109,11 @@ class Conductor: ObservableObject { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func removeLiveTimer(id: TimerID) { |
|
|
|
|
// Logger.log("removeLiveTimer") |
|
|
|
|
|
|
|
|
|
self.liveTimers.removeAll(where: { $0.id == id }) |
|
|
|
|
self.cancelledCountdowns.removeAll(where: { $0 == id }) |
|
|
|
|
self.currentStopwatches.removeValue(forKey: id) |
|
|
|
|
// self.pausedCountdowns.removeValue(forKey: id) |
|
|
|
|
|
|
|
|
|
if let soundPlayer = self._delayedSoundPlayers[id] { |
|
|
|
|
FileLogger.log("Stop sound player: \(self._timerName(id))") |
|
|
|
|
soundPlayer.stop() |
|
|
|
|
@ -168,7 +156,6 @@ class Conductor: ObservableObject { |
|
|
|
|
|
|
|
|
|
self._scheduleCountdownNotification(countdown: countdown, in: totalDuration, handler: handler) |
|
|
|
|
|
|
|
|
|
// TODO: live activity |
|
|
|
|
let end = Date(timeIntervalSinceNow: totalDuration) |
|
|
|
|
self._launchLiveActivity(timer: countdown, date: end) |
|
|
|
|
|
|
|
|
|
@ -214,8 +201,6 @@ class Conductor: ObservableObject { |
|
|
|
|
let start = Date() |
|
|
|
|
let end = start.addingTimeInterval(interval) |
|
|
|
|
|
|
|
|
|
// let countdownId = countdown.stringId |
|
|
|
|
|
|
|
|
|
FileLogger.log("schedule countdown \(step.name ?? "''") at \(end)") |
|
|
|
|
Logger.log("schedule countdown \(step.name ?? "''") at \(end)") |
|
|
|
|
|
|
|
|
|
@ -249,7 +234,6 @@ class Conductor: ObservableObject { |
|
|
|
|
self.cancelSoundPlayers(id: id) |
|
|
|
|
|
|
|
|
|
self._recordAndRemoveCountdown(countdownId: id, cancel: true) |
|
|
|
|
// self.pausedCountdowns.removeValue(forKey: id) |
|
|
|
|
|
|
|
|
|
if Preferences.playCancellationSound { |
|
|
|
|
self._playCancellationSound() |
|
|
|
|
@ -271,10 +255,6 @@ class Conductor: ObservableObject { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// func isCountdownPaused(_ countdown: Countdown) -> Bool { |
|
|
|
|
// return self.pausedCountdowns[countdown.stringId] != nil |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
func remainingPausedCountdownTime(_ countdown: Countdown) -> TimeInterval? { |
|
|
|
|
guard let sequence = self.currentCountdowns[countdown.stringId] else { |
|
|
|
|
return nil |
|
|
|
|
@ -283,16 +263,12 @@ class Conductor: ObservableObject { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func pauseCountdown(id: TimerID) { |
|
|
|
|
Logger.log("Pause countdown") |
|
|
|
|
guard let sequence = self.currentCountdowns[id] else { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Logger.log("Pause countdown") |
|
|
|
|
sequence.pauseDate = Date() |
|
|
|
|
|
|
|
|
|
// let remainingTime = sequence.currentSpan.end.timeIntervalSince(Date()) |
|
|
|
|
// self.pausedCountdowns[id] = Date() //remainingTime |
|
|
|
|
|
|
|
|
|
// cancel stuff |
|
|
|
|
self.cancelCurrentNotifications(countdownId: id) |
|
|
|
|
self.cancelSoundPlayers(id: id) |
|
|
|
|
@ -301,16 +277,11 @@ class Conductor: ObservableObject { |
|
|
|
|
|
|
|
|
|
func resumeCountdown(id: TimerID) throws { |
|
|
|
|
|
|
|
|
|
// let now = Date() |
|
|
|
|
|
|
|
|
|
let context = PersistenceController.shared.container.viewContext |
|
|
|
|
if let countdown: Countdown = context.object(stringId: id), |
|
|
|
|
let sequence = self.currentCountdowns[countdown.stringId], |
|
|
|
|
let pauseDate = sequence.pauseDate { |
|
|
|
|
|
|
|
|
|
// let pauseDuration = now.timeIntervalSince(pauseDate) |
|
|
|
|
|
|
|
|
|
// var steps: [CountdownStep] = [] |
|
|
|
|
for countdownStep in sequence.steps { |
|
|
|
|
if countdownStep.end > pauseDate, let step = countdownStep.step(context: context) { |
|
|
|
|
do { |
|
|
|
|
@ -320,17 +291,12 @@ class Conductor: ObservableObject { |
|
|
|
|
Logger.error(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// steps.append(countdownStep.pauseAdjustedStep(duration: pauseDuration)) |
|
|
|
|
} |
|
|
|
|
sequence.resume() |
|
|
|
|
|
|
|
|
|
// self.currentCountdowns[countdown.stringId] = CountdownSequence(steps: steps) |
|
|
|
|
|
|
|
|
|
// _ = try self._scheduleSoundPlayer(countdown: countdown, in: remainingTime) |
|
|
|
|
} else { |
|
|
|
|
throw AppError.timerNotFound(id: id) |
|
|
|
|
} |
|
|
|
|
// self.pausedCountdowns.removeValue(forKey: id) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fileprivate func _recordAndRemoveCountdown(countdownId: String, cancel: Bool) { |
|
|
|
|
@ -406,19 +372,6 @@ class Conductor: ObservableObject { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// do { |
|
|
|
|
// |
|
|
|
|
// let sound: Sound = countdown.someSound ?? Sound.default |
|
|
|
|
// let soundPlayer = try DelaySoundPlayer(sound: sound) |
|
|
|
|
// self._delayedSoundPlayers[countdownId] = soundPlayer |
|
|
|
|
// |
|
|
|
|
// // TODO: RESTORE |
|
|
|
|
// try soundPlayer.restore(for: sequence.interval.end, repeatCount: Int(countdown.repeatCount)) |
|
|
|
|
// FileLogger.log("Restored sound player for \(self._timerName(countdownId))") |
|
|
|
|
// } catch { |
|
|
|
|
// Logger.error(error) |
|
|
|
|
// } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -453,7 +406,6 @@ class Conductor: ObservableObject { |
|
|
|
|
let content = UNMutableNotificationContent() |
|
|
|
|
content.title = NSLocalizedString("It's time!", comment: "") |
|
|
|
|
|
|
|
|
|
// let duration = countdown.duration |
|
|
|
|
let body: String |
|
|
|
|
if let name = countdown.activity?.name { |
|
|
|
|
let timesup = NSLocalizedString("Time's up for %@!", comment: "") |
|
|
|
|
@ -475,7 +427,6 @@ class Conductor: ObservableObject { |
|
|
|
|
|
|
|
|
|
fileprivate func _createNotification(countdown: Countdown, in duration: TimeInterval, offset: TimeInterval = 0.0, content: UNMutableNotificationContent, handler: @escaping (Result<Date?, Error>) -> Void) { |
|
|
|
|
|
|
|
|
|
// let duration = countdown.duration |
|
|
|
|
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: duration + offset, repeats: false) |
|
|
|
|
|
|
|
|
|
let identifier: String = [countdown.objectID.uriRepresentation().absoluteString, "\(offset)"].joined(separator: Conductor.notificationIdSeparator) |
|
|
|
|
@ -600,30 +551,6 @@ class Conductor: ObservableObject { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// MARK: - Intent |
|
|
|
|
|
|
|
|
|
// fileprivate func _createTimerIntent(_ timer: AbstractTimer) { |
|
|
|
|
// let intent = LaunchTimerIntent() |
|
|
|
|
// |
|
|
|
|
// let invocationPhrase = String(format: NSLocalizedString("Launch %@", comment: ""), timer.displayName) |
|
|
|
|
// intent.suggestedInvocationPhrase = String(format: invocationPhrase, timer.displayName) |
|
|
|
|
// intent.timer = TimerIdentifier(identifier: timer.stringId, display: timer.displayName) |
|
|
|
|
// |
|
|
|
|
// let interaction = INInteraction(intent: intent, response: nil) |
|
|
|
|
// interaction.donate() |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
// fileprivate func _scheduleAppRefresh(countdown: Countdown) { |
|
|
|
|
// let request = BGAppRefreshTaskRequest(identifier: BGTaskIdentifier.refresh.rawValue) |
|
|
|
|
// request.earliestBeginDate = Date(timeIntervalSinceNow: countdown.duration) |
|
|
|
|
// do { |
|
|
|
|
// try BGTaskScheduler.shared.submit(request) |
|
|
|
|
// print("request submitted with date: \(String(describing: request.earliestBeginDate))") |
|
|
|
|
// } catch { |
|
|
|
|
// Logger.error(error) |
|
|
|
|
// } |
|
|
|
|
// } |
|
|
|
|
|
|
|
|
|
// MARK: - Live Activity |
|
|
|
|
|
|
|
|
|
fileprivate func _launchLiveActivity(timer: AbstractTimer, date: Date) { |
|
|
|
|
|