From 676fb0e97bbe36d0b23749ca259dcdba30314934 Mon Sep 17 00:00:00 2001 From: Laurent Date: Sun, 3 Dec 2023 17:51:34 +0100 Subject: [PATCH] wip and fix --- LaunchWidget/LaunchWidgetLiveActivity.swift | 60 ++++++++++++++----- LeCountdown/Conductor.swift | 14 +++-- LeCountdown/CountdownSequence.swift | 39 ++++++++++-- LeCountdown/Views/DialView.swift | 10 ++-- LeCountdown/Views/TimersView.swift | 2 +- .../Widget/LaunchWidgetAttributes.swift | 3 +- 6 files changed, 96 insertions(+), 32 deletions(-) diff --git a/LaunchWidget/LaunchWidgetLiveActivity.swift b/LaunchWidget/LaunchWidgetLiveActivity.swift index 7dc4a7f..419ea07 100644 --- a/LaunchWidget/LaunchWidgetLiveActivity.swift +++ b/LaunchWidget/LaunchWidgetLiveActivity.swift @@ -29,25 +29,34 @@ struct LiveActivityView: View { struct LaunchWidgetLiveActivity: Widget { + fileprivate let now: Date = Date() + var body: some WidgetConfiguration { ActivityConfiguration(for: LaunchWidgetAttributes.self) { context in - // Lock screen/banner UI goes here - VStack(alignment: .leading) { +// TimelineView(.periodic(from: self.now, by: 0.1)) { _ in - if context.attributes.isTimer { - let range = Date()...context.attributes.date - Text(timerInterval: range, - pauseTime: range.lowerBound) - .font(.title) - } else { - Text(context.attributes.date, style: .timer) + // Lock screen/banner UI goes here + VStack(alignment: .leading) { + + let date: Date = self._date(context: context) + let name: String = self._name(context: context) + + if context.attributes.isCountdown { + let range = Date()...date + Text(timerInterval: range, + pauseTime: range.lowerBound) .font(.title) + } else { + Text(context.attributes.date, style: .timer) + .font(.title) + } + Text(name.uppercased()) + .font(.callout) + } - Text(context.attributes.name.uppercased()) - .font(.callout) - - } + +// } .padding() .monospaced() .background(Color(white: 0.1)) @@ -60,10 +69,12 @@ struct LaunchWidgetLiveActivity: Widget { DynamicIslandExpandedRegion(.leading) { Text(context.attributes.name.uppercased()) .monospaced() + .padding(.leading, 4.0) } DynamicIslandExpandedRegion(.trailing) { Text(context.attributes.date, style: .timer) .monospaced() + .padding(.trailing, 4.0) } DynamicIslandExpandedRegion(.bottom) { Button { @@ -76,7 +87,7 @@ struct LaunchWidgetLiveActivity: Widget { Text(context.attributes.name.uppercased()) } compactTrailing: { Group { - if context.attributes.isTimer { + if context.attributes.isCountdown { let range = Date()...context.attributes.date Text(timerInterval: range, pauseTime: range.lowerBound) @@ -85,7 +96,7 @@ struct LaunchWidgetLiveActivity: Widget { } }.multilineTextAlignment(.trailing) } minimal: { - if context.attributes.isTimer { + if context.attributes.isCountdown { let range = Date()...context.attributes.date Text(timerInterval: range, pauseTime: range.lowerBound) @@ -100,9 +111,26 @@ struct LaunchWidgetLiveActivity: Widget { } } + fileprivate func _name(context: ActivityViewContext) -> String { + if let name = context.state.sequence?.currentStep.name { + return name + } else { + return context.attributes.name + } + } + + fileprivate func _date(context: ActivityViewContext) -> Date { + if let date = context.state.sequence?.currentStep.interval.end { + return date + } else { + return context.attributes.date + } + } + fileprivate func _stop() { } + } struct LaunchWidgetLiveActivity_Previews: PreviewProvider { @@ -110,7 +138,7 @@ struct LaunchWidgetLiveActivity_Previews: PreviewProvider { static let attributes = LaunchWidgetAttributes( id: "", name: "Tea", - date: Date().addingTimeInterval(3600.0), isTimer: true) + date: Date().addingTimeInterval(3600.0), isCountdown: true) static let contentState = LaunchWidgetAttributes.ContentState(ended: false) diff --git a/LeCountdown/Conductor.swift b/LeCountdown/Conductor.swift index 8f86ec9..5a25bd6 100644 --- a/LeCountdown/Conductor.swift +++ b/LeCountdown/Conductor.swift @@ -93,8 +93,8 @@ class Conductor: ObservableObject { fileprivate func _buildLiveTimers() { let liveCountdowns: [LiveTimer] = self.currentCountdowns.map { id, sequence in - let currentSpan = sequence.currentSpan - return LiveTimer(id: id, name: currentSpan.label, date: currentSpan.end) + let currentStep = sequence.currentStep + return LiveTimer(id: id, name: currentStep.label, date: currentStep.end) } // add countdown if not present for liveCountdown in liveCountdowns { @@ -217,6 +217,7 @@ class Conductor: ObservableObject { // let countdownId = countdown.stringId FileLogger.log("schedule countdown \(step.name ?? "''") at \(end)") + Logger.log("schedule countdown \(step.name ?? "''") at \(end)") let sound = step.someSound ?? countdown.someSound ?? Sound.default @@ -300,7 +301,7 @@ class Conductor: ObservableObject { func resumeCountdown(id: TimerID) throws { - let now = Date() +// let now = Date() let context = PersistenceController.shared.container.viewContext if let countdown: Countdown = context.object(stringId: id), @@ -325,7 +326,6 @@ class Conductor: ObservableObject { // self.currentCountdowns[countdown.stringId] = CountdownSequence(steps: steps) - // TODO: RESUME // _ = try self._scheduleSoundPlayer(countdown: countdown, in: remainingTime) } else { throw AppError.timerNotFound(id: id) @@ -628,12 +628,14 @@ class Conductor: ObservableObject { fileprivate func _launchLiveActivity(timer: AbstractTimer, date: Date) { + guard let sequence = self.currentCountdowns[timer.stringId] else { return } + if #available(iOS 16.2, *) { if ActivityAuthorizationInfo().areActivitiesEnabled { - let contentState = LaunchWidgetAttributes.ContentState(ended: false) - let attributes = LaunchWidgetAttributes(id: timer.stringId, name: timer.displayName, date: date, isTimer: timer is Countdown) + let contentState = LaunchWidgetAttributes.ContentState(ended: false, sequence: sequence) + let attributes = LaunchWidgetAttributes(id: timer.stringId, name: timer.displayName, date: date, isCountdown: timer is Countdown) let activityContent = ActivityContent(state: contentState, staleDate: nil) do { diff --git a/LeCountdown/CountdownSequence.swift b/LeCountdown/CountdownSequence.swift index a10188f..6f38b44 100644 --- a/LeCountdown/CountdownSequence.swift +++ b/LeCountdown/CountdownSequence.swift @@ -8,7 +8,8 @@ import Foundation import CoreData -class CountdownSequence: Codable { +class CountdownSequence: Codable, Equatable, Hashable { + var steps: [CountdownStep] var pauseDate: Date? = nil @@ -17,7 +18,7 @@ class CountdownSequence: Codable { self.pauseDate = pauseDate } - var currentSpan: CountdownStep { + var currentStep: CountdownStep { let referenceDate = self.pauseDate ?? Date() let current: CountdownStep? = self.steps.first { span in return span.interval.start < referenceDate && span.interval.end > referenceDate @@ -26,7 +27,7 @@ class CountdownSequence: Codable { } var currentEnd: Date { - return self.currentSpan.interval.end + return self.currentStep.interval.end } var dateInterval: DateInterval { @@ -67,11 +68,23 @@ class CountdownSequence: Codable { self.pauseDate = nil } + // MARK: - Equatable / Hashable + + static func == (lhs: CountdownSequence, rhs: CountdownSequence) -> Bool { + return lhs.steps == rhs.steps && lhs.pauseDate == rhs.pauseDate + } + + func hash(into hasher: inout Hasher) { + hasher.combine(steps) + hasher.combine(pauseDate) + } + private static let defaultSpan = CountdownStep(interval: DateInterval(start: Date(), end: Date()), name: "none", index: 0, loopCount: 1, stepId: "") } -class CountdownStep: Codable { +class CountdownStep: Codable, Equatable, Hashable { + var interval: DateInterval var name: String? var index: Int16 @@ -115,4 +128,22 @@ class CountdownStep: Codable { self.interval = DateInterval(start: start, end: end) } + // MARK: - Equatable / Hashable + + static func == (lhs: CountdownStep, rhs: CountdownStep) -> Bool { + return lhs.interval == rhs.interval + && lhs.name == rhs.name + && lhs.index == rhs.index + && lhs.loopCount == rhs.loopCount + && lhs.stepId == rhs.stepId + } + + func hash(into hasher: inout Hasher) { + hasher.combine(interval) + hasher.combine(name) + hasher.combine(index) + hasher.combine(loopCount) + hasher.combine(stepId) + } + } diff --git a/LeCountdown/Views/DialView.swift b/LeCountdown/Views/DialView.swift index b13ec36..b5d5df6 100644 --- a/LeCountdown/Views/DialView.swift +++ b/LeCountdown/Views/DialView.swift @@ -67,10 +67,10 @@ struct DialView: View { } .background(self._dialBackgroundColor) - .frame(width: frameSize, height: self._height) + .frame(width: self.frameSize, height: self._height) .cornerRadius(20.0) } - + fileprivate var _height: CGFloat { return UIDevice.isPhoneIdiom ? 80.0 : 200.0 } @@ -94,13 +94,15 @@ struct DialView: View { Group { switch self.timer { case let countdown as Countdown: - CountdownDialView(countdown: countdown, isEditing: self.isEditingBinding.wrappedValue) + CountdownDialView(countdown: countdown, + isEditing: self.isEditingBinding.wrappedValue) .environmentObject(Conductor.maestro) case let alarm as Alarm: AlarmDialView(alarm: alarm) .environmentObject(Conductor.maestro) case let stopwatch as Stopwatch: - StopwatchDialView(stopwatch: stopwatch, isEditing: self.isEditingBinding.wrappedValue) + StopwatchDialView(stopwatch: stopwatch, + isEditing: self.isEditingBinding.wrappedValue) .environmentObject(Conductor.maestro) default: Text("missing dial view") diff --git a/LeCountdown/Views/TimersView.swift b/LeCountdown/Views/TimersView.swift index 51b0c07..2a35047 100644 --- a/LeCountdown/Views/TimersView.swift +++ b/LeCountdown/Views/TimersView.swift @@ -34,7 +34,7 @@ struct TimersView: View { GeometryReader { reader in let columns: [GridItem] = self._columns() - let width: CGFloat = reader.size.width / CGFloat(columns.count) - 5.0 + let width: CGFloat = max(reader.size.width / CGFloat(columns.count) - 5.0, 0.0) ScrollView { diff --git a/LeCountdown/Widget/LaunchWidgetAttributes.swift b/LeCountdown/Widget/LaunchWidgetAttributes.swift index e470f88..4282534 100644 --- a/LeCountdown/Widget/LaunchWidgetAttributes.swift +++ b/LeCountdown/Widget/LaunchWidgetAttributes.swift @@ -12,12 +12,13 @@ struct LaunchWidgetAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { // Dynamic stateful properties about your activity go here! var ended: Bool + var sequence: CountdownSequence? = nil } // Fixed non-changing properties about your activity go here! var id: String var name: String var date: Date - var isTimer: Bool + var isCountdown: Bool }